Поддерживание контекстной информации
И «учет размеров областей данных», и «обработка EOF» требуют, чтобы в OCB, передаваемом вашей функции io_read() , поддерживалась контекстная информация — в частности, поле offset .
Обновление информации POSIX
И еще одно заключительное соображение: при чтении данных из ресурса должна обновляться POSIX-переменная времени доступа atime («access time» — «время доступа»). Это делается для того, чтобы клиентская функция stat() могла обнаружить, что к устройству кто-то обращался.
Собственно код
Ниже приведена программа, в которой учтены все вышеперечисленные моменты. Ниже мы ее последовательно проанализируем.
/*
* io.read1.c
*/
#include
#include
#include
#include
// наша строка с данными
char* data_string = "Здравствуй, мир!\n";
int io_read(resmgr_context_t* ctp, io_read_t* msg,
iofunc_ocb_t* ocb) {
int sts;
int nbytes;
int nleft;
int off;
int xtype;
struct _xtype_offset* xoffset;
// 1) Проверить, открыто ли устройство на чтение
if ((sts ==
iofunc_read_verify(ctp, msg, ocb, NULL)) != EOK) {
return sts;
}
// 2) проверить и обработать переопределение XTYPE
xtype = msg->i.xtype & _IO_XTYPE_MASK;
if (xtype == _IO_XTYPE_OFFSET) {
xoffset = (struct _xtype_offset*)(msg->i + 1);
off = xoffset->offset;
} else if (xtype = _IO_XTYPE_NONE) {
off = ocb->offset;
} else { // Неизвестный тип; игнорировать
return ENOSYS;
}
// 3) Сколько байт осталось?
nleft = ocb->attr->nbytes – off;
// 4) Сколько байт мы можем отдать клиенту?
nbytes = min(nleft, msg->i.nbytes);
// 5) Если возвращаем данные, отдать их клиенту
if (nbytes) {
MsgReply(ctp->rcvid, nbytes, data_string+off, nbytes);
// 6) Установить значение "atime" для POSIX stat()
ocb->attr->flags |=
IOFUNC_ATTR_ATIME | IOFUNC_ATTR_DIRTY_TIME;
// 7) Если индекс lseek() не равен _IO_XTYPE_OFFSET,
// увеличить его на число считанных байт
if (xtype == _IO_XTYPE_NONE) {
ocb->offset += nbytes;
}
} else {
// 8) Не возвращаем данные, просто разблокировать клиента
MsgReply(ctp->rcvid, EOK, null, 0);
}
// 9) Сказать библиотеке, что мы уже ответили сами
return _RESMGR_NOREPLY;
}
Этап 1
Здесь мы убедились, что клиентский вызов open() действительно запросил открытие устройства на чтение. Если бы клиент открыл устройство только на запись, а затем попытался выполнить чтение, это следовало бы расценивать как ошибку. В этом случае вспомогательная функция iofunc_read_verify() возвратила бы нам (затем мы — библиотеке, а библиотека — клиенту) EBADF, а не EOK.
Этап 2
Здесь мы проверили, указал ли клиент индивидуальное для данного сообщения переопределение типа ( xtype-override ) (например, потому что если мы открыли устройство в неблокирующем режиме, то это указало бы, что для данного конкретного запроса мы хотим задать блокирующее поведение). Отметим, что блокирующий аспект переопределенияа типа может быть отражён в последнем параметре функции iofunc_read_verify() , однако, поскольку мы приводим здесь упрощенный пример, мы передаем NULL, указывая этим, что этот вопрос нас не волнует.
Более важно, однако, посмотреть, как обрабатываются конкретные модификаторы xtype. Очень интересен, например, модификатор _IO_XTYPE_OFFSET, который, если присутствует, указывает на то, что принятое от клиента сообщение содержит смещение, и что операция чтения не должна изменять «текущую позицию файла» для данного файлового дескриптора (так делает, например, функция pread() ). Если модификатор _IO_XTYPE_OFFSET не указан, то операция чтения может смело модифицировать «текущую позицию файла». Мы используем переменную хtype для сохранения xtype, содержавшегося в принятом сообщении, и переменную off для представления текущего смещения, которое мы должны будем использовать при обработке. Далее, на этапе 7, вы увидите еще кое-какие действия по обработке модификатора _IO_XTYPE_OFFSET.
Если присутствует иное переопределение xtype, чем _IO_XTYPE_OFFSET (и это не пустая команда _IO_XTYPE_NONE), мы отказываемся обрабатывать запрос и возвращаем ENOSYS. Это просто означает, что мы не знаем, как обрабатывать такую ситуацию, и поэтому возвращаем клиенту признак ошибки.
Этапы 3 и 4
Чтобы вычислить, сколько байт мы можем реально возвратить клиенту, мы выполняем этапы 3 и 4, в которых выясняется, сколько байт доступно у устройства (разность между полным объемом устройства, полученным из ocb->attr->nbytes
, и текущим смещением в устройстве). Узнав, сколько байт осталось, мы выбираем наименьшее значение между размером этого остатка и количеством байт, которые клиент хочет прочитать. Например, у нас может остаться семь байт, а клиент захочет прочитать только два. В этом случае мы возвратим клиенту только два байта. И наоборот, если клиент захочет прочитать 4096 байт, а у нас осталось только семь, мы сможем возвратить ему только семь байт.
Читать дальше