Читать книгу 📗 "Операционная система UNIX - Робачевский Андрей Михайлович"
Передача данных
Как уже обсуждалось, передача данных в потоке происходит в виде сообщений. Процесс инициирует передачу данных с помощью системных вызовов write(2) и putmsg(2), которые непосредственно взаимодействуют с головным модулем. Головной модуль формирует сообщение, копируя в него прикладные данные, и передает его следующему модулю вниз по потоку. В конечном итоге сообщение принимается драйвером, который выполняет необходимые операции с конкретным устройством. В случае, когда драйвер получает данные от устройства, он также передает их в виде сообщений вверх по потоку. Процесс имеет возможность получить данные с помощью системных вызовов read(2) или getmsg(2). Если в головном модуле данные отсутствуют, процесс блокируется и переходит в состояние сна.
Сообщения передаются модулями с помощью системной функции putnext(9F):
#include <sys/stream.h>#include <sys/ddi.h>int putnext(queue_t *q, mblk_t *mp);Эта функция адресует очередь следующего модуля параметром
q<i>xx</i>put()mp<i>xx</i>put()Передача данных внутри потока осуществляется асинхронно и не может блокировать процесс. Блокирование процесса возможно только при передаче данных между процессом и головным модулем. Таким образом, функции обработки данных потока —
<i>xx</i>put()<i>xx</i>service()<i>xx</i>put()<i>xx</i>service()<i>xx</i>service()<i>xx</i>service()Процедура
<i>xx</i>service()<i>xx</i>service()<i>xx</i>put()<i>xx</i>service()Блокирование недопустимо и для драйвера. Обычно прием данных драйвером осуществляется с использованием прерываний. Таким образом процедура
<i>xx</i>put()Когда процедура
<i>xx</i>put()#include <sys/stream.h>int putq(queue_t *q, mblk_t *mp);Функция putq(9F) помещает сообщение mp в очередь q, где сообщение ожидает последующей передачи, и заносит очередь в список очередей, нуждающихся в обработке. Для таких очередей ядро автоматически вызывает процедуру
<i>xx</i>service()<i>xx</i>service()runqueues()runqueues()□ Когда какой-либо процесс выполняет операцию ввода/вывода над потоком.
□ Непосредственно перед переходом какого-либо процесса из режима ядра в режим задачи.
Заметим, что планирование обслуживания очередей не связано с конкретным процессом и производится для всей подсистемы STREAMS в целом.
Функция
runqueue()<i>xx</i>service()<i>xx</i>service()runqueue()Управление передачей данных
Деление процесса передачи данных на два этапа, выполняемых, соответственно, функциями
<i>xx</i>put()<i>xx</i>service()Как уже упоминалось, обязательной для модуля является лишь функция
<i>xx</i>put()<i>xx</i>service()<i>xx</i>put()<i>xx</i>put()xxput(queue_t *q, mblk_t *mp) { putnext(q, mp);}
Рис. 5.19. Передача данных без управления потоком
Когда данные достигают драйвера, он передает их непосредственно устройству. Если устройство занято, или драйвер не может немедленно обработать данные, сообщение уничтожается. В данном примере никакого управления потоком не происходит, и очереди сообщений не используются.
Хотя такой вариант может применяться для некоторых драйверов (как правило, для псевдоустройств, например, /dev/null), в общем случае устройство не может быть все время готово к обработке данных, а потеря данных из-за занятости устройства недопустима. Таким образом, в потоке может происходить блокирование передачи данных [60], и эта ситуация не должна приводить к потере сообщений, во избежание которой необходим согласованный между модулями механизм управления потоком. Для этого сообщения обрабатываются и буферизуются в соответствующей очереди модуля, а их передача возлагается на функцию
<i>xx</i>service()