phantom-84 писал(а):
Мне кажется, что асинхронный ввод-вывод сам по себе сложнее (требует гораздо большей усидчивости при написании программ), в добавок приложениям потребуется самостоятельно выполнять синхронизацию, когда это необходимо (а это необходимо практически всегда). Короче сложно.
Для начала разделим это самое "короче сложно" на два "вида сложности": для ядра (включая сюда драйверы) и для задач. Ядро по-любому имеет дело с реальной, т.е. асинхронной природой ввода-вывода, а значит, никакой "лишней" сложности здесь быть не может в принципе (более того, чисто асинхронная обработка в непрерываемом ядре окажется значительно проще, чем "принудительно синхронная" в прерываемом, о чём я уже неоднократно писал, но что при необходимости можно обсудить дополнительно). Остаётся сложность для задач.
Действительно, для среднестатистического человека уследить за несколькими одновременно выполняющимися процессами достаточно сложно, и программисты здесь не исключение. По этой причине применение асинхронного ввода-вывода в прикладной программе обычно сложнее, чем применение синхронного. Однако, во-первых, есть задачи (обычно довольно сложные -- во всяком случае, существенно превосходящие по сложности "хелловорлд"), которые если не проще, то уж точно эффективнее решать с помощью асинхронного ввода-вывода. А вот во-вторых, и это в контексте "сложности" самое главное, асинхронный ввод-вывод элементарно превращается в синхронный, не вызывая никаких сложностей. Об этом я уже писал, но повторюсь в несколько упрощённом виде:
1. Поток запускает операцию ввода-вывода, указав системе, что после завершения этой операции надо установить некое событие.
2. Сразу после запуска операции (которая выполняется асинхронно по отношению к потоку!) поток вызывает системный сервис ожидания, в котором указывает это же событие.
В результате поток сразу после запуска ввода-вывода "засыпает" до тех пор, пока ввод-вывод не будет завершён, т.е. для него эта операция ввода-вывода выполняется синхронно.
Более того, вместо двух вызовов системы можно сделать объединённый вызов, который сначала запускает ввод-вывод, а затем сразу же переводит поток в ожидание события, связанного с завершением ввода-вывода. Так сделано, например, в RSX-11 и VAX/VMS, где имеется два практически одинаковых системных сервиса QIO$ и QIOW$ с полностью идентичным набором параметров. Разница только в том, что QIOW$ сразу переведёт задачу (в тамошней терминологии) в ожидание завершения ввода-вывода, ну а QIO$ позволит ей работать дальше одновременно с выполнением ввода-вывода и "затормозить" только тогда, когда это реально необходимо, применив один из сервисов ожидания.
Таким образом, наличие асинхронного ввода-вывода не усложняет жизнь прикладному программисту, а лишь даёт ему намного больше возможностей для оптимального решения стоящих перед ним задач. Не хочет программист связываться с асинхронностью -- пожалуйста, переводи поток сразу в ожидание окончания ввода-вывода, и будет тебе синхронность. Хочет извлечь преимущества из асинхронного ввода-вывода -- тоже пожалуйста, всё в твоих руках. Причём, если нужен синхронный ввод-вывод, эта самая синхронность достигается без всяких усилий (а значит, дополнительных сложностей) и со стороны ядра, и со стороны потока пользователя; в частности, нет никакой нужды делать ядро прерываемым потоками пользователя, а значит, заморачиваться вопросом обеспечения корректного возобновления его выполнения, отсутствия потенциальных взаимных блокировок и т.д. и т.п.
Цитата:
Может, опишешь модель работы приложения, использующего асинхронный ввод-вывод, в сравнении, скажем, с двухпоточным приложением, где один поток занимается файловыми операциями, а другой консольными операциями.
Такое приложение -- это некий сфероконь в вакууме. Если почётче сформулировать общую идею (т.е.
что должно делаться), я могу написать,
как это следует делать. Однако не факт, что для данного конкретного случая асинхронный ввод-вывод будет наиболее оптимальным решением: это ж не фетиш какой, которому нужно непременно поклоняться, а лишь инструмент, который нужно применять тогда, когда он удобен, и использовать что-то иное, если он неудобен.
Цитата:
Ну например функцию чтения сектора можно разделить на две функции в соответствии с фазами данной операции - подачей команды на чтение и приемом результатов.
Никаких проблем здесь не возникает. Когда драйвер начинает операцию, он выдаёт устройству нужную команду, помечает в своих собственных управляющих блоках, какая операция запущена и т.д. и т.п., и разрешает прерывания от этого устройства, после чего прекращает работу. Что с этого момента выполняется, непринципиально. Это может быть какой-либо поток пользователя, код ядра, код другого драйвера -- неважно.
Когда устройство закончит операцию, оно кинет прерывание, драйвер его получит, посмотрит, чем он там занимался, и предпримет необходимые действия по завершению ввода-вывода. В частности, через стандартную подпрограмму менеджера ввода-вывода драйвер удалит уже завершённый запрос ввода-вывода и установит событие, связанное с завершившейся операций.
Собственно, именно так и работает система ввода-вывода в Винде, поскольку она туда перекочевала из VAX/VMS, куда, свою очередь, попала из RSX-11. Правда, с каждым "перебазированием" этот механизм всё раздувался, усложнялся и дополнялся, причём не всегда обоснованно ("хотели как лучше..."), но всё равно сам по себе остался быстрым и эффективным, намного превосходя любые унихи (собсно, одна из причин катастрофического отставания унихов по производительности от RSX-11 и VAX/VMS крылась именно в переусложнённом ядре с синхронным вводом-выводом, хотя, конечно, свою лепту внесли и другие причины -- использование Си вместо ассемблера, например). Другое дело, что Винда была сильно "усовершенствована" в других моментах, из-за чего в целом превратилась в неповоротливого монстра, но к идеологии ввода-вывода это прямого отношения не имеет...