Re: fprintf && write

From
Boris Rudakov (2:5054/9.4)
To
Lev Walkin ()
Date
2003-06-05T09:58:36Z
Area
RU.UNIX.PROG
Hello Lev!

05 июн 03 00:49, you wrote to Andrey Melnikov:


 LW> Andrey Melnikov wrote:

[...]
 >> 1. Получить засыпание при вызове fprintf(...) где-то в ядре на
 >> write(..) на очень большое время, что увы неприемлимо. 2.
 >> Использовать alarm(time), write()/fprintf(), alarm(0); - я получу 3
 >> системных вызова.

 LW> а попробуй так:
 LW> 1. при открытии дескриптора переводи его в non-blocking mode.

О !!!

А можно это место немножечко подробнее ?

Я как-то в этой эхе уже спрашивал о юниксоидной методологии асинхронного IO, но ответы были хотя вполне внятные, но я таки не все сказанное догнал :):):):)

Типовая (наитиповейшая, я бы сказал) задачка такова:
Есть нитка, которая развлекается асинхронным чтением (можно и записью), и попутно ждет команды идти на в связи с шатдауном.


"Вражеский" :):):) код, к примеру, выглядит так (сорри за размер, но тут все важно, т.к. мне интересны юниксоидные аналоги всех приведенных ниже приседаний):

...
HANDLE hStopEvent = CreateEvent(0, FALSE, FALSE, 0); // Команда идти на
OVERLAPPED Overlap;  // Управление async IO, внутренний ивент инициализирован
...

Читаем:

..... (HANDLE hFile, LPBYTE Buf, WORD Len)
{
  DWORD W;
  while (Len) {
    ...Тут чистим Overlap в зависимсти от типа файла, но всегда -
    ResetEvent(Overlap.hEvent);
    BOOL Ok = ReadFile(hFile, Buf, Len, &W, &Overlap);
    if (W) { // Ok may be FALSE, but W may be != 0 если pending IO, хех :)
      Len -= W;
      Buf += W;
    }
    while (!Ok) {
      switch (GetLastError()) { // Аналог errno
        case ERROR_IO_PENDING: break; // Асинхронное безобразие in progress...

        case ERROR_BROKEN_PIPE:         // Эти три взбрыка означают одно и
        case ERROR_NO_DATA:             // то же но для разных типов файлов;
        case ERROR_NETNAME_DELETED:     // лучше проверять все
                                        // универсальности кода для :)
          // Connection lost = TRUE; Все плохо...
          return FALSE;
        default:
          // Прочий взбрык, пишем ошибку куда надо и
          CancelIo(hFile); // Побашкелопатана !
          return FALSE;
      }
      // *Самое интересное* - ждем одно из двух событий
      HANDLE hWait[] = {hStopEvent, Overlap.hEvent};
      switch (WaitForMultipleObjects(2, hWait, FALSE, INFINITE)) {
        case 0: // Нам велели идти на - yes my lord...
          CancelIo(hFile); // Побашкелопатана !
          return FALSE;
        case 1: // В файле чего-то зашевелилось...
          Ok = GetOverlappedResult(hFile, &Overlap, &W, TRUE);
          if (W) { // Снова Ok may be FALSE, но W != 0 из-за pending IO
            Len -= W;
            Buf += W;
          }
          ...Снова чистим Overlap, но всегда -
          ResetEvent(Overlap.hEvent);
          break; // Прогоним внутренний цикл чтобы посмотреть ошибку...
        default:
         // Сбой ожидания - кто-то грохнул hStopEvent или Overlap.hEvent
         CancelIo(hFile); // Побашкелопатана !
         return FALSE;
      }
    }
  }
  return TRUE; // Все, Len bytes из hFile успешно высосаны
}

Реинкарнации этого кода могут варьироваться, но суть всегда одна и та же:
* Инициируем операцию IO, имеем в виду что она может пролететь сразу же вся (данные упихались в буфера ядра и асинхронность не потребовалась) - выходим
* Операция прошла не успешно - смотрим чего стряслось. Если это ERROR_IO_PENDING - пошло асинхронное IO и надо им озаботиться
* Делаем GetOverlappedResult чтобы узнать статус асинхронного IO, он тоже может сказать FALSE но снова выставить ERROR_IO_PENDING - ядро продолжает асинхронную операцию
* Чем заняться пока идет IO - глубоко пофигу: когда операция завершится ядро просигналит Overlapped.hEvent и последующий GetOverlappedResult скажет TRUE. Наш код сидит в WaitForMultipleObjects и интересуетя еще и командой прекратить операцию

Надо заметить, что буфера (по крайней мере у НТи) достаточно велики и обычно такой код ограничивается одноим только ReadFile, в pending проваливаясь достаточно редко. Как управлять буферами дисковых файлов я не помню, буфера пайпов задаются при их создании, буфера же сокетов по дефолту 4k и управляются обычным sockopt.

Я привел этот здоровый кусок "вражеского" кода чтобы поспрошать как такое делается в юниксе.

1. Как перевести хендл файла в асинхронный режим ?

2. select - хорошо, но я встречал мало кода где он используется, эээээ..., скажем так - аккуратно используется :) В смысле что используется строго при необходимости тогда и только тогда, когда ясно что асинхроная операция требует внимания потому как чего-то уже прокачалось.

3. Как можно ждать нескольких РАЗНОРОДНЫХ СОБЫТИЙ ???? Сколько я ни читал про юникс API я как-то не нашел ничего, равного по мощи и универсильности виндозному WaitForMultipleObjects. В данном примере я упихал туда ивент отвечающий за команду завершения (некая нить супервизор просто сделает SetEvent(hStopEvent) и данный код прекратит IO поймав это событие) и ивент из Overlapped (его взведет ядро когда асинхронное чтение закончится). А вообще я могу засунуть туда хэндл любого объекта ядра - нити или процесса (их просигналят когда нить / процесс завершатся) и ты ды и ты пы.

Как такие вещи правильно делать в юниксе ?

 LW> 2. В подобных твоему участках кода делай _сначала_ write(), а уж
 LW> потом, если write() вернул -1/EAGAIN, то select:

Т.е. write(...) => -1, errno == EAGAIN - это то же самое что
WriteFile(...) => FALSE, GetLastError() == ERROR_IO_PENDING ?

 LW> while() {
 LW>   if(write()) {
 LW>     select();

Если write() > 0 то зачем select ?
Подождать пока пройдет следующая порция IO ?

 LW>     ...
 LW>   }
 LW> }

А как мне подождать не только IO complette, а еще и чего-то внешнего, типа hStopEvent в моем примере ?

 LW> Тогда имеется вероятность, что за один вызов write() управишься в
 LW> большом количестве случаев.

 LW> --
 LW> Lev Walkin
 LW> vlm@netli.com


Борис Рудаков,               ...мы плохо кончим все - какая разница с кем ?
BBR

--- Be happy: BBR is looking at you !
 * Origin: АлкАголь малыми дозами безвреден в любых количествах (2:5054/9.4)