Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.12.153.31] |
|
Сообщ.
#1
,
|
|
|
Доброго времени суток.
Вопрос про кросплатформенное программирование, код на FreePascal/Lazarus, но сам вопрос скорее про Linux, поэтому я как-то даже теряюсь в каком разделе спрашивать. Впринципе знаю C/C++ поэтому перевести с одного языка на другой проблем не составит. Есть код под Windows для обмена с COM-портом использующий Overlapped запись/чтение и дальнейшее ожидание пришедших данных с использованием WinApi WaitForMultipleObjects. Код выполняется в отдельном потоке. Пытаюсь перевести его под Linux. Загвостка в том что этот WaitForMultipleObjects используется для ожидания двух Event'ов - один от ввода/вывода, другой - для оповещения потока о завершении приложения. Вычитал что под Linux есть неблокирующие операции ввода вывода, но как я понял select не позволяет ожидать какие-либо события помимо ввода вывода (мб плохо читал?). aio_... в fpc не нашел, поэтому остановился именно на неблокирующих операциях. Также вычитал что в Linux можно назначить процедуру, которая будет вызываться при запросе на завершение потока, но я так понимаю что она скорее всего будет задействована какими-то внутренними механизмами встроенной библиотеки компилятора, поэтому её использование не желательно. Сам в Linux не силен от слова совсем, может кто подскажет как из этого лучше выкрутиться или ткнёт что почитать. Код под Windows прилагаю. Подрезал чтобы не такая большая "простыня" была, но впринципе в нём нет ничего особенного. const DEADLOCK_TIMEOUT = 60000; BUFSIZE = 512; var buf: array [0..BUFSIZE-1] of Byte; cb: DCB; ct: COMMTIMEOUTS; ov: OVERLAPPED; hPort: THandle; hCancelEvent: THandle; wo: array [0..1] of THandle; bRet: BOOL; nRet: DWORD; dwBytesRead: DWORD; begin hPort := CreateFile('\\.\COM1', GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); try ov.Internal := 0; ov.InternalHigh := 0; ov.Offset := 0; ov.OffsetHigh := 0; ov.hEvent := CreateEvent(nil, TRUE, FALSE, nil); try hCancelEvent := CreateEvent(nil, TRUE, FALSE, nil); try // заполнение cb bRet := SetCommState(hPort, @cb); // запонение ct bRet := SetCommTimeouts(hPort, @ct); bRet := ReadFile(hPort, Pointer(@buf[0])^, BUFSIZE, dwBytesRead, @ov); // ... wo[0] := ov.hEvent; wo[1] := hCancelEvent; nRet := WaitForMultipleObjects(2, @wo[0], FALSE, DEADLOCK_TIMEOUT); if nRet = WAIT_OBJECT_0 + 0 then begin bRet := GetOverlappedResult(hPort, ov, dwBytesRead, FALSE); // ... end else if nRet = WAIT_OBJECT_0 + 1 then begin CancelIO(hPort); end; // ... finally CloseHandle(hCancelEvent); end; finally CloseHandle(ov.hEvent); end; finally CloseHandle(hPort); end; end; |
Сообщ.
#2
,
|
|
|
Сообщ.
#3
,
|
|
|
Ну да, примерно это же я всё уже читал. Ещё вычитал что select можно прервать путём отправки "сигнала", тогда он прервется и вернет EINTR.
Размышляя пришел к такой формулеровке вопроса как "отмена блокировки", т.е. если ты используешь операцию которая блокирует выполнение на не определенный промежуток времени, то неплохо было бы иметь возможность её отменить. Для систем типа DOS в которых нет многозадачности это не имеет смысла, т.к. впринципе не существует другого процесса/потока который может затребовать отмену этой самой блокировки. Для многозадачных это становится актуальным. Под винду это как раз решается добавлением дополнительного Event'а, а вот под Linux ничего толкового не ищется - прерывать поток не рекомендуют, т.к. последствия теже что и винде - замусоривание. Единственное что более менее стабильное и безоспасное так это предложение крутить select с таймаутом в цикле, переодически прерываясь и проверяя что оно там вернуло. В таком случае таймаут имеет смысл ставить так чтобы попадать в итерации планировщика задач - меньше не имеет смысла, т.к. поток всё равно не будет выполняться два раза за цикл, а если будет, то это приведет к тому что он будет отбирать время у других задач. Ещё такой момент: под виндой у порта есть ReadIntervalTimeout, который я тоже использую, под Linux'ом я ничего такого не нашел и видимо придется городить огород, впринципе это сочетается с вариантом крутить select в цикле. |
Сообщ.
#4
,
|
|
|
Цитата Step @ .. Под винду это как раз решается добавлением дополнительного Event'а, а вот под Linux ничего толкового не ищется - прерывать поток не рекомендуют, т.к. последствия теже что и винде - замусоривание. Единственное что более менее стабильное и безоспасное так это предложение крутить select с таймаутом в цикле, переодически прерываясь и проверяя что оно там вернуло. В таком случае таймаут имеет смысл ставить так чтобы попадать в итерации планировщика задач - меньше не имеет смысла, т.к. поток всё равно не будет выполняться два раза за цикл, а если будет, то это приведет к тому что он будет отбирать время у других задач. Да, я понимаю, такие же вопросы возникали и у меня. --- Не пробовал, но можно попробовать - в качестве дополнительного Event-а в Линукс использовать семафор. (как-то я эту потенциальную возможность упустил. Надо будет попробовать.) Если у тебя найдётся время и возможность - расскажи, что получилось. --- Если сработает T-OUT select-a это, конечно, затратит время. Но не так чтобы очень много. Бывает, что у Виндуса я это делаю (по разным причинам). Минус предложенного варианта даже не в затраченном времени, а в задержке реакции на событие. Об этом и в упомянутых статьях тоже не забыли написать. |