Управление вводом и выводом
Этот раздел содержит вводную информацию о программировании ввода-вывода и обзор функций управления вводом-выводом.
Функции библиотеки ввода-вывода предназначены для чтения данных из файлов (или получения от устройств) и записи данных в файл (или пересылки на устройство). Устройства рассматриваются системой как файлы ввода-вывода. Например, устройство необходимо открывать и закрывать точно так же, как и файл.
Некоторые функции используют для ввода-вывода данных стандартные файлы (устройства) ввода-вывода. Однако для большинства функций вы можете определять свои собственные файлы для ввода и вывода данных. Для многих функций можно использовать указатель на файл, т.е. на структуру, содержащую имя файла; для других вы можете использовать дескриптор файла (положительный целый идентификатор, присваиваемый файлу при его открытии).
#include <stdio.h>Некоторые функции библиотеки ввода-вывода - это макрокоманды, определенные в файле заголовка, а некоторые - это объектные модули функций. Во многих случаях библиотека содержит макрокоманду и функцию, которые выполняют одну и ту же операцию. Выбирая между макрокомандой и функцией, учтите следующее:
- Для макрокоманды нельзя установить контрольную точку с помощью программы dbx.
- Макрокоманды обычно выполняются быстрее, чем функции, так как препроцессор заменяет макрокоманду на строки кода.
- При компиляции макрокоманд создается более громоздкий объектный код.
- При работе с функциями могут возникать побочные эффекты.
Файлы, команды и функции, применяемые для управления вводом-выводом, предоставляют следующие типы интерфейсов:
- Низкий уровень
- Низкоуровневый интерфейс предоставляет базовые функции открытия и закрытия файлов и устройств.
- Поток
- Потоковый интерфейс предоставляет функции чтения и записи для каналов и файлов FIFO.
- Терминал
- Интерфейс терминала обеспечивает форматированный вывод и буферизацию.
- Асинхронный кабель
- Асинхронный интерфейс обеспечивает одновременное выполнение операций ввода-вывода и обработки.
- Язык ввода
- Интерфейс языка ввода применяет команды lex и yacc для создания лексического анализатора, а также программу анализатора для интерпретации ввода-вывода.
Низкоуровневые интерфейсы ввода-вывода
Низкоуровневый интерфейс ввода-вывода - это точка непосредственного входа в ядро. Она предоставляет такие функции, как открытие и закрытие файлов, чтение из файла и запись в файл.
Для чтения одной строки из стандартного ввода предназначена команда line. Другие операции низкоуровневого ввода-вывода выполняются следующими функциями:
- open, openx или creat
- Подготовка файла или другого объекта каталога для чтения или записи путем присвоения ему дескриптора файла
- read, readx, readv или readvx
- Чтение данных из открытого файла с указанным дескриптором
- write, writex, writev или writevx
- Запись данных в открытый файл с указанным дескриптором
- close
- Освобождение дескриптора файла
Функции open и creat помещают записи в три системные таблицы. В первую таблицу, выполняющую функции области данных (доступ к которой имеют функции чтения и записи) для процесса, записываются индексы дескрипторов файлов. Каждая запись в этой таблице содержит указатель на соответствующую запись во второй таблице.
Вторая таблица - это база данных системы, или таблица файлов, которая позволяет нескольким процессам совместно использовать общие файлы. Запись в этой таблице указывает режим доступа к файлу (файл был открыт для чтения, для записи или как канал) или момент закрытия файла. Кроме того, запись содержит смещение, указывающее, где будет происходить следующая операция чтения или записи, и указатель на запись в третьей таблице, которая содержит копию индексного дескриптора (i-узла) файла.
В таблице файлов содержатся записи для всех экземпляров функции open или create в файле, а в таблицу i-узлов заносится только по одной записи для каждого файла.
При выполнении операции чтения или записи для определения перечисленных ниже переменных используются аргументы, введенные пользователем, и информация, содержащаяся в записи таблицы файлов:
- Пользовательский адрес целевой области ввода-вывода
- Счетчик передаваемых байтов
- Текущее положение в файле
Если операции ввода-вывода выполняются над специальным текстовым файлом, то для передачи данных и обновления счетчика байтов и текущего положения указателя в файле вызывается соответствующая функция чтения или записи. В противном случае, текущее положение используется для вычисления номера логического блока в файле.
Если обрабатывается обычный файл, то номер логического блока должен быть преобразован в номер физического блока. Преобразовывать специальный блочный файл не нужно. Полученный номер физического блока используется для чтения данных с устройства или их записи.
Блочные драйверы устройств позволяют передавать информацию без использования буфера, непосредственно от пользовательского исполняемого модуля к устройству и обратно, причем размер буфера определяется в запросе вызывающей программы. Этот метод включает определение специального текстового файла, соответствующего устройству прямого доступа, и вызов функций чтения-записи для создания частного (не общего) заголовка буфера с соответствующей информацией. При необходимости могут вызываться отдельные функции открытия и закрытия, а также особая функция для работы с магнитной лентой.
Потоковые интерфейсы ввода-вывода
Потоковые интерфейсы, представляющие данные в виде потоков байтов, не интерпретируемых системой, более эффективны с точки зрения сетевых протоколов, чем символьные интерфейсы. При чтении и записи потока данных не происходит его деление на отдельные записи. Например, процесс, прочитавший 100 байт из некоторого канала, не может определить, были ли эти данные записаны сразу, либо было выполнено две операции по 50 байт, или же эти данные были записаны двумя различными процессами.
Потоковый ввода-вывод может быть организован в виде каналов или файлов FIFO ("первым вошел - первым вышел"). Файлы FIFO схожи с каналами, поскольку данные в них могут двигаться только в одном направлении (слева направо). Однако, в отличие от канала, файлу FIFO можно присваивать имя и к нему могут обращаться не связанные с ним процессы. Иногда файлы FIFO называют именованными каналами. Поскольку у файла FIFO есть имя, его можно открывать с помощью стандартной функции fopen. Процедура открытия канала сложнее: необходимо вызвать функцию pipe, возвращающую дескриптор файла, а затем с помощью стандартной функции ввода-вывода fdopen связать дескриптор открытого файла со стандартным потоком ввода-вывода.
Доступ к потоковым интерфейсам ввода-вывода можно получить с помощью следующих функций и макрокоманд:
- fclose
- Закрывает поток
- feof, ferror, clearerr или fileno
- Проверяет состояние потока
- fflush
- Записывает все буферизованные на данный момент символы из потока
- fopen, freopen или fdopen
- Открывает поток
- fread или fwrite
- Выполняет двоичный ввод
- fseek, rewind, ftell, fgetpos или fsetpos
- Перемещает указатель файла в потоке
- getc, fgetc, getchar или getw
- Извлекает символ или слово из входного потока
- gets или fgets
- Получает строку из потока
- getwc, fgetwc или getwchar
- Извлекает "широкий" символ из входного потока
- getws или fgetws
- Получает строку из потока
- printf, fprintf, sprintf, wsprintf, vprintf, vfprintf, vsprintf или vwsprintf
- Печатает форматированный вывод
- putc, putchar, fputc или putw
- Помещает символ или слово в поток
- puts или fputs
- Записывает строку в проток
- putwc, putwchar или fputwc
- Помещает символ или слово в поток
- putws или fputws
- Помещает строку "широких" символов в поток
- scanf, fscanf, sscanf или wsscanf
- Преобразует форматированный ввод
- setbuf, setvbuf, setbuffer или setlinebuf
- Выделяет буфер для потока
- ungetc или ungetwc
- Возвращает извлеченный символ обратно во входной поток
Терминальные интерфейсы ввода-вывода
Интерфейсы терминального ввода-вывода осуществляют взаимодействие между процессом и ядром, выполняя такие функции, как буферизация и форматирование вывода. Для каждого терминала или псевдотерминала существует структура tty, содержащая ИД текущей группы процесса. Это поле определяет группу процессов, получающих сигналы, связанные с данным терминалом. Доступ к терминальным интерфейсам ввода-вывода можно получить с помощью команды iostat, контролирующей загрузку системного устройства ввода-вывода, и демона uprintfd, позволяющего выводить сообщения ядра на экран терминала.
Ниже перечислены функции, позволяющие включать или отключать определенные параметры терминала:
- cfgetospeed, cfsetospeed, cfgetispeed или cfsetispeed
- Считывает и устанавливает значение скорости передачи в бодах
- ioctl
- Выполняет функции управления терминалом
- termdef
- Запрашивает характеристики терминала
- tcdrain
- Ожидает завершения вывода
- tcflow
- Выполняет функции управления потоком
- tcflush
- Очищает указанную очередь
- tcgetpgrp
- Возвращает идентификатор интерактивной группы процессов
- tcsendbreak
- Передает сигнал прерывания по асинхронной последовательной линии
- tcsetattr
- Задает состояние терминала
- ttylock, ttywait, ttyunlock или ttylocked
- Управляет функциями блокировки терминала
- ttyname и isatty
- Получает имя терминала
- ttyslot
- Выполняет поиск участка в файле utmp для текущего пользователя
Асинхронные интерфейсы ввода-вывода
Функции асинхронного ввода-вывода позволяют процессу запускать операцию ввода-вывода и сразу после запуска операции или постановки в очередь выходить из функции. Если необходимо, чтобы процесс ожидал завершения операции ввода-вывода (или немедленно получал управление обратно, если операция уже завершена), то требуется другая функция. Таким образом, в режиме асинхронного ввода-вывода процесс может совмещать операции обработки и ввода-вывода или выполнять ввод-вывод на несколько устройств одновременно. Применение режима асинхронного ввода-вывода практически не влияет на быстродействие процесса, который считывает данные из файла на диске и записывает их в другой файл на диске, но значительно повышает производительность других типов программ ввода-вывода, например, программ копирования содержимого диска на магнитную ленту или создания изображения на графическом дисплее.
Можно установить режим, при котором ядро будет уведомлять процесс, выполняющий асинхронный ввод-вывод, о готовности определенного дескриптора к вводу-выводу (так называемый ввод-вывод по сигналу). При работе с LEGACY AIO, для уведомления пользовательского процесса ядро использует сигнал SIGIO. При работе с POSIX AIO, для уведомления пользовательского процесса ядро использует структуру sigevent. Применяются следующие сигналы: SIGIO, SIGUSR1 и SIGUSR2.
Для работы в режиме асинхронного ввода-вывода процесс должен выполнить следующие действия:
- Установить обработчик сигнала SIGIO. Этот шаг необходим только в том случае, если установлен режим уведомления с помощью сигнала.
- Установить ИД процесса или ИД группы процессов, которые будут принимать сигналы SIGIO. Этот шаг необходим только в том случае, если установлен режим уведомления с помощью сигнала.
- Включить режим асинхронного ввода-вывода. Обычно решение о том, включать ли (загружать ли) этот режим, принимает системный администратор. Включение режима происходит при запуске системы.
Предусмотрены следующие функции асинхронного ввода-вывода:
- aio_cancel
- Отменяет один или несколько ожидающих запросов асинхронного ввода-вывода
- aio_error
- Получает информацию о состоянии ошибки запроса асинхронного ввода-вывода
- aio_fsync
- Синхронизирует асинхронные файлы.
- aio_nwait
- Приостанавливает вызывающий процесс до выполнения определенного числа запросов ввода-вывода.
- aio_read
- Считывает информацию в асинхронном режиме из файла с указанным дескриптором
- aio_return
- Определяет код возврата запроса асинхронного ввода-вывода
- aio_suspend
- Переводит вызывающий процесс в состояние ожидания, пока не завершится один или несколько запросов асинхронного ввода-вывода
- aio_write
- Записывает информацию в асинхронном режиме в файл с указанным дескриптором
- lio_listio
- Инициализирует список запросов асинхронного ввода-вывода посредством одного вызова
- poll и select
- Проверяет состояние ввода-вывода нескольких дескрипторов файлов и очередей сообщений
Для работы с функцией poll необходимо включить в программу следующие заголовочные файлы:
- poll.h
- Определяет структуры и флаги, используемые функцией poll
- aio.h
- Определяет структуры и флаги, используемые функциями aio_read, aio_write и aio_suspend