Билет 07 - honeycarbs/bmstu-os-6sem GitHub Wiki
Классификация типов ввода-вывода с точки зрения программиста: диаграммы последовательности действий для каждого типа ввода-вывода и описание. Классификация моделей ввода-вывода. Особенности и назначение асинхронного ввода-вывода. Мультиплексирование. Пример мультиплексирования для сокетов AF_INET, SOCK_STREAM. Сетевой стек. Пример (лаб. раб.)
Модели ввода-вывода
Блокирующий ввод-вывод
Асинхронный ввод-вывод невозможен для обычных файлов, т.е. при работе с обычными файлами всегда будут блокировки. Запросив ввод-вывод, приложение блокируется и оно пробуждается, когда внешнее устрйоство завершает операцию ввода-вывода, формирует сигнал прерывания, который приходит на контроллер прерывания и этот сигнал с контроллера прерывания в самой простейшей схемы поступает на выделенную ножку процессора в конце выполнения каждой команды процессор проверяет наличия сигнала прерывания на этой ножке, если он пришел -процессор переходит на обработку, для этого он должен адресовать прерывание, для этого в системе есть IDT.

Неблокирующий ввод-вывод
Везде используем recfrom(), а не read/write, это показатель того, что речь идет не об обычных файлах, например может идти о пайпах, передаче сообщений, о сокетах. В данном случае мы видим, что процесс не блокируется, но постоянно запрашивает данные. Получив ошибку, что данные не готовы, он опять выполняет запрос. Это крайне затратная схема. Процессор контроллирует процесс ввода-вывода, постоянно опрашивая готовность внешнего устройства, соответственно готовность устанавливается устройством с помощью флагов, т.е. процессор постоянно опрашивает флаги готовности внешнего устройства. Когда устройство готово, данные аппаратаным прерыванием будут записаны в буфер ядра, а потом каким-то отложенным действием они будут в дальнейшем дообработаны и доведены до приложения.

Мультиплексирование ввода-вывода
Мультиплексер (коммутатор) - устройство, которое объединяет информацию, поступающую по нескольким каналам ввода, и выдает ее по одному выходному каналу. Процесс совмещения нескольких сообщений, передаваемых одновременно, в одной физической или логической среде.
Существует 2 основных вида мультиплексирования: временнóе и частотное.

Приложение-клиент обращается к сокету, для этого вызывает системный вызов connect, чтобы установить связь с приложением-сервером. Здесь сеодинены запросы клиентов и работа на стороне сервера. Системный вызов select или poll выполняется на стороне сервера, клиенты устанавливают соединение. Более подробно:

Смысл мультплексирования.
Некоторый процесс блокируется при вызове select, ожидая когда сокет станет доступным для чтения. Затем ядро возвращает приложению статус readable, сообщая, что можно получать данные помощью recvfrom. Снова блокировка, да еще вместо одного два системных вызова (select и recvfrom), что увеличивает накладные расходы. Но в отличии от рассмотренного ранее блокирующего метода, select или любой другой мультиплексор обеспечивает возможность ожидать данные не от одного, а от нескольких файловых дескрипторов, что, очевидно, снижает время блокировки (сна).
Клиенты пытаются создать соединение с сервером, вызывая системный вызов connect. В результате создается пул дескрипторов сокетов. Получение процессом-клиентом EINPROGRESS означает, что соединение устанавливается. На работу сервера получение клиентом EINPROGRESS никак не влияет, так как мультиплексор обработает первое соединение, которое произойдет: в цикле проверяются все сокеты и берется первый, который готов. Пока соединение принимается (accept), поступают другие соединения. Таким образом снижается время простоя, так как первый раз ожидание может затянуться, но последующие соединения требуют значительно меньших задержек.
Модель ввода-вывода, управляемого сигналом
Сигнал SIG_IO должен быть определен в системе, соотв. функция sigaction может установить собственный обработчик этого сигнала или можно использовать обработчик по умолчанию.
Это асинхронный ввод-вывод, и процесс продолжает выполняться. Он блокируется только для того, чтобы дождаться получения данных.
Для того, чтобы реализовать такой асинхронный ввод-вывод, ядро должно взять на себя всю работу, у сигнала SIG_UO есть обработчик, который ждет возникнования сигнала, работу по посылке этого сигнала выполняет ядро, которое отслеживает готовность данных. Когда данные
готовы, ядро пошлет сигнал SIG_IO, в результате будет вызван обработчик этого сигнала, при этом функцию recfrom можно вызывать либо в обработчике сигнала, либо в основном потоке приложения.
Сигнал типа SIG_IO для каждого процесса может быть только один. В результате, используя сигнал SIG_IO, можно работать только с одним файловым дескриптором.

Модель асинхронного ввода-вывода
Для такого ввода-вывода используются специальные команды, которые называются aio_read, aio_write, и тд. Проблема асинхронного ввода: определить, что может делать приложение, не получив данные? Время выполнения действий соответствующим приложением будет меньше, т.е. отзывчивость такой системы может быть меньше при правильном написании.

Особенности и назначение асинхронного ввода-вывода.
Асинхронный ввод-вывод (AIO) - это метод выполнения операций ввода-вывода таким образом, что процесс, выдавший запрос ввода-вывода, не блокируется до завершения операции. Вместо этого после отправки запроса ввода-вывода процесс продолжает выполняться его код и можно позже проверить статус поданного запроса. Для завершения транзакции ввода-вывода может использоваться сигнал, на который устанавливается обработчик сигнала, или поток на основе обратного вызова.
Мультиплексирование. Пример мультиплексирования для сокетов AF_INET, SOCK_STREAM. Сетевой стек. Пример (лаб. раб.)
Мультиплексирование ввода-вывода (вызов select() или poll()) означает, что работа процесса блокируется до поступления ожидаемых данных или до перехвата какого-либо сигнала, то есть они представляют собой синхронную форму уведомления.
Сетевой стек
В Linux определен интерфейс между пользовательскими процессами и стеком сетевых протоколов в ядре.
Модули протоколов группируются по семействам протоколов, такими, как AF_INET, AF_IPX и AF_PACKET, и типам сокетов, такими, как SOCK_STREAM или SOCK_DGRAM. Сетевой стек ядра Linux имеет две структуры:
- struct socket — интерфейс высокого уровня, который используется для системных вызовов (именно поэтому он также имеет указатель struct file, который представляет файловый дескриптор)
- struct sock — имплементация в ядре для AF_INET сокетов (есть также struct unix_sock для AF_UNIX сокетов, которые являются производными от данного), которые могут использоваться как в ядре, так и в пользовательском пространстве.
Если сокеты используются для обмена данными на одной и той же машине, передаваемые данные должны пройти все уровни сетевого стека, что отрицательно сказывается на быстродействии и повышает нагрузку на систему.
Примеры мультиплексоров
Пример ретранслирующего TCP-сервера на основе вызова select():
fd_set set; /* набор файловых дескрипторов */
FD_ZERO(&set); /* сбросить в наборе все биты */
FD_SET(sd, &set); /* поместить дескриптор в набор */
for (i = 0; i < MAX_CLIENTS; i++)
{
if (clients[i] > 0)
FD_SET(clients[i], &set);
max_sd = (clients[i] > max_sd) ? (clients[i]) : (max_sd);
}
struct timeval timeout = {15, 0}; /* зададим таймер для закрытия соединения по таймауту */
rc = select(max_sd + 1, &set, NULL, NULL, &timeout);
if (rc == 0)
{
printf("\nServer closed connection by timeout.\n\n");
close(sd);
return 0;
}
else if (rc == -1)
{
perror("can't select()");
return EXIT_FAILURE;
}
if (FD_ISSET(sd, &set))
connection_handler(sd);
for (int i = 0; i < MAX_CLIENTS; i++)
{
fd = clients[i];
if ((fd > 0) && FD_ISSET(fd, &set))
client_handler(fd, i);
}