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)