Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: 1 [2] 3  Все   Вниз
  Печать  
Автор Тема: Реализация буфера данных в USB-драйвере.  (Прочитано 115876 раз)
0 Пользователей и 1 Гость смотрят эту тему.
jur
Помогающий

lt
Offline Offline

« Ответ #30 : 21-09-2010 13:22 » 

jur, тогда уж и REALTIME_PRIORITY_CLASS процессу имеет смысл назначить...

Ну... Весь процесс целиком может и не быть столь критичным. У меня, к примеру, именно так. Потоки, работающие после получения данных, могут прерываться без проблем. Главное - получить эти данные вовремя (в моей системе со стороны "железа" стоит буфер только на два пакета).

а вообще я в приоритеты "не верю") по моему ИМХО, для правильно распланированной программы - никакие приоритеты не нужны)

Не, они реально работают. Опять же, сошлюсь на свой опыт. Как приоритет считывателя сделаю обычным - тут же пакеты пропадают :-)
Записан

MPEG-4 - в массы!
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #31 : 21-09-2010 14:43 » 

общий приоритет складывается из приоритета процесса и нити. там такая двухступенчатая шкала) посмотрите на msdn.
а насчет работает... по моему тут больше вопрос размера буфера)
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

« Ответ #32 : 21-09-2010 16:12 » 

общий приоритет складывается из приоритета процесса и нити. там такая двухступенчатая шкала) посмотрите на msdn.

У меня сложилось впечатление, что Билли наш Гейц - хитрейший мужик! :-) Что и как он позаимствовал из Юникса по вопросу планирования процессов - никто не знает. Поэтому, IMHO, MSDN нужно читать между строк :-)

а насчет работает... по моему тут больше вопрос размера буфера)

У меня буфер (на приемной стороне) стандартный, 256 пакетов (по 512 байт каждый). Пакеты представляют собой ультразвуковые лучи (УЗИ), отображаемые на экране. Понятно, что очень важно, чтобы никакие лучи не пропадали. Практика показала, что при нормальном приоритете лучи пропадают, а при повышенном - нет. Вот я и сделал такой вывод :-)
Записан

MPEG-4 - в массы!
PredatorAlpha
Помогающий

us
Offline Offline

« Ответ #33 : 21-09-2010 19:34 » 

Просмотрел тему, может несколько поверхностно, но складывается впечатление непродуманности архитектур.
В одном случае совершенно неприемлемо, что в зависимости от нагрузки устройство не успевает обрабатывать DPC на однопроцессорной машине. Архитектура должна быть построена так, что бы драйвер гарантировано получал всё без пропусков на однопроцессорной, пусть даже и на все запущенное пользовательского уровня и не останется процессора. Тогда можно гарантировать, что на двухпроцессорном будет всё отлично работать. А когда всё тормозит если что-то большое запустили на двухпроцессорном - это абздец. Мало ли что можно запустить...
Я конечно понимаю, что от программиста мало зависит, когда система даст управление DPC, но если уже период вызова DPC критично важен, значит нужно искать другие методы. Использовать ISR, например. Неверняка там предусмотрен callback для драйверов конечных устройств из ISR контроллера после получения порции данных. Если нет - значит его нужно организовать самому, любым образом. В конце концов, скопировать 60 (даже и 512) байт можно и из ISR (и пусть весь мир подождёт!).
Во втором случае, совершенно неприемлимо, что на пользовательский процесс (вызов readfile) что-то завязано.  readfile не должен нести никакой функциональной части, кроме доставки данных в пользовательский процесс. Я конечно понимаю, что используется готовое, и оно не сильно предназначено для "скоростных" устройств, но чувство неправильности такого подхода остаётся...
« Последнее редактирование: 21-09-2010 21:07 от PredatorAlpha » Записан
jur
Помогающий

lt
Offline Offline

« Ответ #34 : 22-09-2010 18:49 » 

Я конечно понимаю, что от программиста мало зависит, когда система даст управление DPC, но если уже период вызова DPC критично важен, значит нужно искать другие методы. Использовать ISR, например. Неверняка там предусмотрен callback для драйверов конечных устройств из ISR контроллера после получения порции данных. Если нет - значит его нужно организовать самому, любым образом. В конце концов, скопировать 60 (даже и 512) байт можно и из ISR (и пусть весь мир подождёт!).

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

Только я этого делать еще не научился :-) Поэтому привел пример своего решения на уровне пользовательской программы, которое в общем-то позволяет решить задачу. Однако мне уже начинает мешать отсутствие умения делать прием данных прямо в драйвере (у меня уже скопилось порядка 4-5 разных видов пакетов, которые правильнее было бы раскладывать в отдельные буфера). А вот приступить к созданию собственного драйвера как-то пока боязно :-)
Записан

MPEG-4 - в массы!
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #35 : 22-09-2010 20:04 » 

jur, длину буфера лучше выражать не количеством пакетов, а скоростью потока.
Размера буферизации(будь то кольцевой буфер, или очередь) должно хватать МИНИМУМ на 200-500мс заполнения с вашей максимальной скоростью потока.
и это действительно МИНИМУМ! вообще говоря, лучше, когда его длины хватает на несколько секунд, по возможности.
максимум - на время запуска "тяжелых" приложений - десятки секунд (но это не всегда реализуемо).

PS откуда такой счет "минумума"(грубо):  размер кванта в винде, помножить на  пять-семь гипотетических процессов(если они все внезапно захотят выжрать весь свой полный квант), + запас в два раза;)

PPS PredatorAlpha, полностью согласен)
« Последнее редактирование: 22-09-2010 20:06 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Bulat
Участник

ru
Offline Offline

« Ответ #36 : 23-09-2010 05:51 » 

IMHO, WaitForSingleObject не очень подходит. Дело в том, что в случае непрогнозируемой скорости поступления данных становится не понятно, каким же выбрать таймаут? Я делаю вот так:
Не совсем понятно как создется массив ивентов. У меня всего два ивента - чтение и запись. Как создать очередь из ивентов чтения, например, не понятно, то есть как различать ивенты чтения между собой. Так я создаю ивенты:
Код:
hEvent_Write = CreateEvent(
NULL,    // default security attribute
TRUE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object

// set up overlapped structure fields
gOverLapped_Write.Offset     = 0;
gOverLapped_Write.OffsetHigh = 0;
gOverLapped_Write.hEvent     = hEvent_Write;

hEvent_Read = CreateEvent(
NULL,    // default security attribute
TRUE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object

// set up overlapped structure fields
gOverLapped_Read.Offset     = 0;
gOverLapped_Read.OffsetHigh = 0;
gOverLapped_Read.hEvent     = hEvent_Read;
То есть у меня всего один ивент чтения hEvent_Read и один ивент записи hEvent_Write. А как тогда заполнить массив ивентов для потока чтения hEventArray_read[QUEUE_SIZE+1] и записи hEventArray_erite[QUEUE_SIZE+1]. Или я неправильно понял?
Цитата
Первым, наиболее приоритетным, ставлю ивент закрытия потока

Мне нет необходимости заводить ивент закрытия потока, так как я его закрываю по флагу и пока этот флаг сброшен поток в бесконечном цикле принимает данные.
И потом ивент чтения возникает же после того, как будет выполнена ф-ия readfile, тогда если WaitForMultipleObjects с атрибутом INFINITE выполнять перед выполнением ф-ии readfile, то разве он дождется события?
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #37 : 23-09-2010 08:42 » 

Цитата
Не совсем понятно как создется массив ивентов.
а в цикле события создать - это так сложно догадаться? и массив хендлов заполнить? Жжешь
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Bulat
Участник

ru
Offline Offline

« Ответ #38 : 25-09-2010 11:31 » 

а в цикле события создать - это так сложно догадаться? и массив хендлов заполнить? Жжешь
Создать, то я создал, только WaitForMultipleObjects  возвращает все время 0-й ивент.
Код:
//Первые ивенты в массивах устанавливаются при создании потоков записи и чтения соответственно
hEvent_Write[0] = CreateEvent(
NULL,    // default security attribute
TRUE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object
hEvent_Read[0] = CreateEvent(
NULL,    // default security attribute
TRUE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object

for(int i=0; i<queuesize; i++)
{

hEvent_Write[i+1] = CreateEvent(
NULL,    // default security attribute
TRUE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object



// set up overlapped structure fields
gOverLapped_Write[i].Offset     = 0;
gOverLapped_Write[i].OffsetHigh = 0;
gOverLapped_Write[i].hEvent     = hEvent_Write[i+1];

hEvent_Read[i+1] = CreateEvent(
NULL,    // default security attribute
TRUE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object



// set up overlapped structure fields
gOverLapped_Read[i].Offset     = 0;
gOverLapped_Read[i].OffsetHigh = 0;
gOverLapped_Read[i].hEvent     = hEvent_Read[i+1];
}

Поток записи:
Код:
UINT TransmitThread( LPVOID pParam )
{

unsigned char buf_w[60];
DWORD recv_index;

BOOL sete = SetEvent(hEvent_Write[0]);  //Создание первого ивента при открытии потока

update = 0;
while(update == 0)
{
DWORD nEvent = WaitForMultipleObjects(queuesize+1, hEvent_Write, FALSE, INFINITE);
if(nEvent > 0 && nEvent <= queuesize)
{
recv_index = nEvent-1;
Success = WriteFile(PipeOut, &buf_w[recv_index], 60, &nBytes, &gOverLapped_Write[recv_index]);
WaitForSingleObject(hEvent_Write[recv_index],INFINITE);
GetOverlappedResult(PipeOut,&gOverLapped_Write[recv_index],&nBytes, FALSE);
}
                                          WR_spd += nBytes;
nBytes=0;

}//while(update == 0)

              return 0;   // thread completed successfully
AfxEndThread(0, TRUE);
}
Так вот, nEvent  у меня всегда 0, аналогично и в потоке чтения..
Записан
resource
Молодой специалист

ru
Offline Offline
Пол: Мужской

« Ответ #39 : 26-09-2010 00:51 » new

Код прямо "черные сказки белой зимы". Мозг разорван на части. Вопрос (риторический): это работает?
И еще очень хотелось бы глянуть на код, который сигналит ивенты.
« Последнее редактирование: 26-09-2010 12:45 от resource » Записан
jur
Помогающий

lt
Offline Offline

« Ответ #40 : 26-09-2010 07:47 » 

Так вот, nEvent  у меня всегда 0, аналогично и в потоке чтения..

Складывается впечатление, что многочисленные ивенты созданы как-то несистемно... И еще важный вопрос: созданные ивенты типа "manual-reset event". А кто их сбрасывает?! Вот и долбится первый ивент до посинения :-)

Мой алгоритм действий при старте потока такой.

1. В hEventArray[0] заношу ивент выхода их потока. Этот ивент с автосбросом.
2. В hEventArray[1..N] заношу ивенты получения очередной порции данных. ВНИМАНИЕ! Эти ивенты я сделал с ручным сбросом потому, что мне нужно их ожидать. Дело в том, что CyAPI тоже пользуется этим ивентом и, если ивент с автосбросом, то мой поток этого события не увидит (он автосбросится внутри CyAPI).

Поэтому я полагаю, что во-первых, нужно все-таки завести ивент прекращения записи/считывания (выхода из потока). Во-вторых, внимательно просмотреть всю цепочку работы с ивентами: произошло событие, получил порцию данных, сбросил ивент, запустил следующую порцию I/O. У меня это уже немало лет нормально работает.
Записан

MPEG-4 - в массы!
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #41 : 26-09-2010 10:40 » 

у вас первый эвент в массиве - hEvent_Write[0].  с ручным сбросом.
и вы его сами перед циклом устанавливаете в сигнальное состояние(хотя он при рождении уже в нем):
Код:
BOOL sete = SetEvent(hEvent_Write[0]); 
и нигде больше не сбрасываете... что ж вы хотите? логично предположить что он всегда будет проверятся первым))) и всегда будет возвращен 0.

не понял зачем вот это ожидание нужно было сразу после записи?:
Код:
WaitForSingleObject(hEvent_Write[recv_index],INFINITE);
GetOverlappedResult(PipeOut,&gOverLapped_Write[recv_index],&nBytes, FALSE);

честно говоря совершенно непонятно что в коде происходит... с бухты барахты какие то эвенты ожидаются... - откуда они вообще приходят? да еще и с ручным сбросом... первый раз такое вижу)
если они с ручным сбросом - они ж там постоянно будут стоять!
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Bulat
Участник

ru
Offline Offline

« Ответ #42 : 28-09-2010 10:17 » 

В соответствии с вышесказанным исправил свой код.
Передача:
Код:
UINT TransmitThread( LPVOID pParam )
{

unsigned char buf_w[60];
DWORD recv_index;

hEvent_Write[0] = CreateEvent(
NULL,    // default security attribute
FALSE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object

for(int i=0; i<queuesize; i++)
{

hEvent_Write[i+1] = CreateEvent(
NULL,    // default security attribute
FALSE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object

// set up overlapped structure fields
gOverLapped_Write[i].Offset     = 0;
gOverLapped_Write[i].OffsetHigh = 0;
gOverLapped_Write[i].hEvent     = hEvent_Write[i+1];
}

nBytes=0;
update = 0;
while(update == 0)
{
//Заполняется буфер buf_w

DWORD nEvent = WaitForMultipleObjects(queuesize+1, hEvent_Write, FALSE, INFINITE);
if(nEvent > 0 && nEvent <= queuesize)
{
recv_index = nEvent-1;
Success = WriteFile(PipeOut, &buf_w, 60, &nBytes, &gOverLapped_Write[recv_index]);
GetOverlappedResult(PipeOut,&gOverLapped_Write[recv_index],&nBytes, FALSE);

if(nBytes) {WR_spd += nBytes; nBytes=0;}
}//if(nEvent > 0 && nEvent <= queuesize)
//}//while(recv_index!=queuesize)

}//while(update == 0)

    return 0;   // thread completed successfully
AfxEndThread(0, TRUE);

}

Прием:
Код:
UINT ReceiveThread( LPVOID pParam )
{

unsigned char buf_r[queuesize][60];
DWORD recv_index=0;

hEvent_Read[0] = CreateEvent(
NULL,    // default security attribute
FALSE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object

for(int i=0; i<queuesize; i++)
{
hEvent_Read[i+1] = CreateEvent(
NULL,    // default security attribute
FALSE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object



// set up overlapped structure fields
gOverLapped_Read[i].Offset     = 0;
gOverLapped_Read[i].OffsetHigh = 0;
gOverLapped_Read[i].hEvent     = hEvent_Read[i+1];
}

stop_r = 0;
RealRead = 0;
while(stop_r == 0)
{
nEvent = WaitForMultipleObjects(queuesize+1, hEvent_Read, FALSE, INFINITE);
if(nEvent > 0 && nEvent <= queuesize)
{
recv_index = nEvent-1;
Success = ReadFile(PipeIn, &buf_r, 60, &RealRead, &gOverLapped_Read[recv_index]);
GetOverlappedResult(PipeIn,&gOverLapped_Read[recv_index],&RealRead, FALSE);

                                if(RealRead) {RD_spd += RealRead; RealRead=0;}
                         }
                          ...
                         //Разбор принятого буфера
          }
         return 0;
AfxEndThread(0, TRUE);
}

В таком виде данные принимаются и передаются.
Но при этом для записи и чтения используется одномерный буфер, который умещает только один пакет. Если буферизовать пакеты, то соответственно нужно вводить двумерный буфер. Например, буфер для чтения: unsigned char buf_r[queuesize][60].
Тогда, операция чтения будет выглядеть следующим образом:
Код:
        while(stop_r == 0)
{
while(recv_index!=queuesize)
{
nEvent = WaitForMultipleObjects(queuesize+1, hEvent_Read, FALSE, INFINITE);
if(nEvent > 0 && nEvent <= queuesize)
{
recv_index = nEvent-1;
Success = ReadFile(PipeIn, &buf_r[recv_index], 60, &RealRead, &gOverLapped_Read[recv_index]);
GetOverlappedResult(PipeIn,&gOverLapped_Read[recv_index],&RealRead, FALSE);
                        }
                 }
         } 
Но в таком виде в буфер buf_r ничего не записывается? При этом ивенты поступают..
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #43 : 28-09-2010 13:00 » 

сделайте пояснения в каждой строчке кода,  иначе мне совершенно не ясно, что вы пытались сделать) по-моему вам и самому это не совсем ясно Ага

а перед этим прочитайте разделSynchronous and Asynchronous I/O (не картинки посмотреть, а именно прочитать до конца) а если поищите - найдете аналог описания и на русском, их полно должно быть в сети.

а так же прочитайте наконец описания функций, которые вы используете (ReadFile и GetOverlappedResult), но теперь уже ЦЕЛИКОМ. и не забывайте про возвращаемые значения. не понимаю, почему их все тотально игнорировать любят?!
пока все ваши ошибки из за невнимательности и нежелания подробно прочесть о используемых вами функциях.
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

« Ответ #44 : 28-09-2010 19:43 » 

Bulat

Я не очень понял насчет записи, поэтому порассуждаю насчет чтения. На мой взгляд, в цикле "for(int i=0; i<queuesize; i++)" следует не просто назначать ивенты, а непосредственно запускать запросы считывания. IMHO, тут логическая ошибка: ивенты создаются, но ничего не организовывается. Повторю еще раз требуемый алгоритм.

Во-первых, создаем ивент для выхода из потока. (Да, да, я не устану об этом талдычить :-) И вот почему. Мы запустили кучу считываний. Ожидаем их завершения. А если данные перестанут поступать? Что делать, как выйти из потока? Вот тут-то и пригодится самый приоритетный, первый ивент. В основной программе мы его установим, дескать, пора линять, а в потоке это увидим. Тогда все красиво завершим и нормально выйдем.)

Во-вторых, запускаем сразу несколько считываний. Согласно размера очереди.

В-третьих, когда очередное считывание выполнилось, получаем принятые данные и тут же снова запускаем следующее считывание для этого слота (элемента, позиции) очереди.

Замечание по поводу размера запроса. Как я уже упоминал, практика показала, что эффективнее запускать считывание не одного пакета, а сразу целой пачки. Соответственно, никаких многомерных массивов не нужно, а просто заводим массив считывания размером <размер пакета>*N, где N - какое-то приемлемое число, например, 8-16-32 и т.п.

Когда драйвер выполнит запрос, мы получим в своем буфере <размер пакета>*N байт принятых данных. Далее можем распоряжаться ими, как требуется.

Не знаю, как более понятно объяснить... Не сомневаюсь, что изучение, компиляция и запуск примера Streamer, обсуждение которого я упоминал, может полностью развеять неясности в вопросе работы с USB-драйвером.

« Последнее редактирование: 28-09-2010 20:12 от jur » Записан

MPEG-4 - в массы!
Bulat
Участник

ru
Offline Offline

« Ответ #45 : 30-09-2010 09:23 » 

Ochkarik
Привожу код с комментариями, а способ обработки возвращаемых значений функции readfile из msdn
Код:
UINT ReceiveThread( LPVOID pParam )
{

unsigned char buf_r[60];
DWORD recv_index=0;
//Создаю ивент, по которому буду выходить из потока
hEvent_Read[0] = CreateEvent(
NULL,    // default security attribute
FALSE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object
        //Создаю ивенты чтения
for(int i=0; i<queuesize; i++)
{
hEvent_Read[i+1] = CreateEvent(
NULL,    // default security attribute
FALSE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object



// set up overlapped structure fields
gOverLapped_Read[i].Offset     = 0;
gOverLapped_Read[i].OffsetHigh = 0;
gOverLapped_Read[i].hEvent     = hEvent_Read[i+1];
}

stop_r = 0;
RealRead = 0;
        //Запускаю бесконечный цикл
while(stop_r == 0)
{
                        //Ожидаю ивенты
                        nEvent = WaitForMultipleObjects(queuesize+1, hEvent_Read, FALSE, INFINITE);

                        //Ивент остановки потока
                        if(nEvent==0) {return 0; AfxEndThread(0, TRUE);}

                        //Ивент чтения
if(nEvent > 0 && nEvent <= queuesize)
{
recv_index = nEvent-1;
                                //Отсылаю запрос на считывание пакета в 60 байт
Success = ReadFile(PipeIn, &buf_r, 60, &RealRead, &gOverLapped_Read[recv_index]);
                           if (!Success)
   {
// deal with the error code
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we have reached the end of the file
// during the call to ReadFile
 
                       // code to handle that
}
 
case ERROR_IO_PENDING:
{
// asynchronous i/o is still in progress
 
                        // do something else for a while
//GoDoSomethingElse() ;
 
// check on the results of the asynchronous read
Success = GetOverlappedResult(PipeIn,&gOverLapped_Read[recv_index],&RealRead, FALSE);
 
// if there was a problem ...
if (!Success)
{
// deal with the error code
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we have reached the end of
// the file during asynchronous
// operation
}
 
// deal with other error cases
}   //end switch (dwError = GetLastError())
}
} // end case
 
// deal with other error cases, such as the default
 
} // end switch (dwError = GetLastError())
            } // end if (!Success)

                         }//if(nEvent > 0 && nEvent <= queuesize)
                          ...
                         //Разбор принятого буфера
          }
         
}

Записан
Bulat
Участник

ru
Offline Offline

« Ответ #46 : 30-09-2010 09:24 » 

jur
Я следовал алгоритму, который вы привели на первой страничке данной темы (Ответ #28), там операция считывания из драйвера в буфер выполнялась после WaitForMultipleObjects. Если смотреть на код приема данных, который я привел в последнем посте, то я там так и сделал.  Во время отладки значение nEvent, которое возвращает WaitForMultipleObjects меняется в каждом цикле от 0 до queuesize и readfile благополучно считывает все данные. Но в вашем последнем посте вы говорите о том, что надо сначало запустить серию запросов чтения данных и ссылаетесь на пример streamer. Но ведь и в вашем коде и в примере streamer используются функции библиотеки CyAPI, работа которых, насколько я понимаю, отличается от WinAPI функций. То есть, если я в цикле запущу сразу несколько функций readfile, не дожидаясь их выполнения, то я потеряю данные.
Код:
                for(int i=0; i<queuesize; i++)
{
Success = ReadFile(PipeIn, &buf_r[i], 60, &RealRead, &gOverLapped_Read[i]);
if (!Success)
{
// deal with the error code
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we have reached the end of the file
// during the call to ReadFile
 
                       // code to handle that
}
 
case ERROR_IO_PENDING:
{
// asynchronous i/o is still in progress
 
                        // do something else for a while
//GoDoSomethingElse() ;
 
// check on the results of the asynchronous read
Success = GetOverlappedResult(PipeIn,&gOverLapped_Read[i],&RealRead, FALSE);
 
// if there was a problem ...
if (!Success)
{
// deal with the error code
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we have reached the end of
// the file during asynchronous
// operation
}
 
// deal with other error cases
}   //end switch (dwError = GetLastError())
}
} // end case
 
// deal with other error cases, such as the default
 
} // end switch (dwError = GetLastError())
} // end if
}
Тут я применяю readfile, как сказано в msdn. Если я после этого цикла буду ожидать ивенты с помощью WaitForMultipleObjects, то я часть ивентов потеряю, так как они придут еще во время выполнения цикла с readfile и я их просто пропущу.
Вот я и не могу понять как можно создать очередь запросов из readfile, если прежде чем отправить следующий запрос readfile, надо обработать результат выполнения предыдущего запроса.
Цитата
Как я уже упоминал, практика показала, что эффективнее запускать считывание не одного пакета, а сразу целой пачки. Соответственно, никаких многомерных массивов не нужно,...
Дело в том, что мой девайс посылает Хосту пакеты по 60 байт и если я в readfile, ставлю считать, например 600 байт, то естественно функция ничего не возвращает. И вот тут опять возникает вопрос, касающийся очереди запросов. В приведенном выше коде используется буфер buf_r, который объявлен следующим образом unsigned char buf_r[60]. Когда мы ReadFile(PipeIn, &buf_r, 60, &RealRead, &gOverLapped_Read), то 60 байт записываются в buf_r, но тогда при каждой итерации i этот буфер будет перезаписываться и данные потеряются, если мы их оттуда не считаем до выполнения следующей итерации. Если записывать  ReadFile(PipeIn, &buf_r, 60,..., тогда не совсем понятно к чему относится индекс i, ведь  buf_r одномерный, а тут он получается вроде как двумерный buf_r[queuesize][60]?
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #47 : 30-09-2010 14:01 » 

мельком глянул, сорри, работа горит, поэтому кратко.

итак, на примере одного запроса:

1. СНАЧАЛА отправить запрос чтения с конкретным event-ом в overlapped. сразу после этого - НА САМОМ ДЕЛЕ - состояние буфера неизвестно! данные могут быть прочитаны а может быть и нет! поэтому:
2. проверить возвращенное значение,
    а) если оно success - запрос обработан - данные в выходном буфере readfile, и вы их должны сохранить для дальнейшего использования у себя.
    б) если вернулся пендинг -  то запрос ПОКА НЕ ОБРАБОТАН! БУФЕР после вызова readfile - ЕЩЕ ПУСТ! в этом случае вы должны ждать event из оверлапеда(то есть ждать завершение обработки операции чтения).  ТОЛЬКО после этого вы можете проверять статус операции readfile (при помощи GetOverlappedResult) - а выполнилась ли операция на самом деле? и только после этого вы можете использовать от массив который вы задавали в качестве входного для той операции readfile.

а у вас что то наоборот получилось: сначала вы ждете событие(кто его будет ставить если запроса еще не было?) потом отправляете запрос чтения для этого же эвента. а используете массив - вообще в непонятный момент, в котором его состояние по большому счету не определено.

кстати еще вопрос - а драйвер вообще асинхронные запросы держит? шо то я забыл как нумега их делает по умолчанию.

и наконец главное... код из msdn скопипастить у вас получилось... но вы хотя бы комментарии в этом коде прочли?)
PS
case ERROR_IO_PENDING:
...
            // asynchronous i/o is still in progress

                           // do something else for a while
            //GoDoSomethingElse() ;
 
            // check on the resultsof the asynchronous read
            Success = GetOverlappedResult(PipeIn,&gOverLapped_Read,&RealRead, FALSE);
             // if there was a problem ...
            if (!Success)
            {
               // deal with the error code
« Последнее редактирование: 30-09-2010 14:23 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

« Ответ #48 : 03-10-2010 11:41 » 

Я следовал алгоритму, который вы привели на первой страничке данной темы (Ответ #28), там операция считывания из драйвера в буфер выполнялась после WaitForMultipleObjects.

Совершенно верно. Предлагаю сначала рассмотреть ситуацию, когда очереди нет, т.е. делается всего один запрос чтения. Посмотрим, что происходит.

1. Запускаем операцию чтения. Драйвер начал свою работу.
2. Ожидаем завершения операции с помощью WaitForMultipleObjects.
3. Просыпаемся по ивенту, что говорит нам о поступлении запрошенных данных.
4. Получаем данные (копируем их в свой буфер).
5. Запускаем следующую операцию чтения.
6. Переходим к пункту 2.

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

Это будет нормально работать без малейших проблем. Однако в ситуации, когда время довольно критично (мой случай), будет непозволительной роскошью позволить драйверу расслабляться :-) (Бери больше, кидай дальше. Пока летит - отдыхай!) Поэтому думаем дальше.

Но в вашем последнем посте вы говорите о том, что надо сначало запустить серию запросов чтения данных и ссылаетесь на пример streamer.

Именно так. Проанализировав приведенный выше алгоритм, мы заметим, что пока мы просыпаемся и работаем с данными, драйвер нагло сычкует! Чтобы этого не допустить как раз и нужна очередь. В пункте 1 мы запускаем сразу несколько запросов. Далее в пункте 2 первый из них будет выполнен, мы просыпаемся и получаем данные. Но в это время драйвер продолжает работу и считывает следующие порции данных! Таким образом мы можем полностью загрузить систему, не допуская непроизводительных простоев процессора.

Но ведь и в вашем коде и в примере streamer используются функции библиотеки CyAPI, работа которых, насколько я понимаю, отличается от WinAPI функций.

Это совсем не так. Если программа работает под Виндой, то ничего кроме WinAPI она использовать не может :-) (Экзотику на уровне драйверов ядра мы не рассматриваем.) По сути CyAPI - это просто удобная оболочка для все тех же родимых обращений к драйверу.

То есть, если я в цикле запущу сразу несколько функций readfile, не дожидаясь их выполнения, то я потеряю данные.

Это еще почему?! Каждый запущенный запрос выполнится и никакой потери данных не будет. Просто нужно, как весьма резонно заметил коллега Ochkarik, весьма корректно и правильно выполнить требования asynchronous i/o.

Кстати, я именно потому и пользуюсь CyAPI, чтобы не ковыряться со всеми этими ERROR_IO_PENDING да GetOverlappedResult :-)

И еще одно замечание. Для начала - хрен с ним, с запросом пачки пакетов. Можно запросить всего один (на 60 байт). Весьма возможно, что и с одним пакетом производительности системы уже хватит. Тут главное - вдумчивая организация очереди запросов и внимательное прочтение ответа #47 коллеги Ochkarik. (Я в этих вопросах не силен, без CyAPI с драйвером USB серьезно работать еще не умею.)
Записан

MPEG-4 - в массы!
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #49 : 03-10-2010 11:55 » 

PS глянул какой-то старый код для usb драйвера на ftdi который я нумегой делал - STATUS_PENDING оно поддерживало... причем, чуть ли не по умолчанию:
        IoCallDriver(DeviceExtension->LowerDeviceObject, Irp);
        status = STATUS_PENDING;
так что... разбирайтесь с асинхронными запросами и будет вам наверное счастье) это ж банальный API) тут (как впрочем и везде) надо просто внимательно читать документацию не пропуская ни строчки)

jur, да на самом деле все точно так же. что CyAPI что просто API... ничего там сверх естественного придумать нельзя) даже особо ничего скрыть не получится... ну чуть побольше if-ов с проверками, но по мне лучше сразу разбираться с первоисточником, чем с чужими "фичами", которые ни в коем случае не "баги")))
« Последнее редактирование: 03-10-2010 11:59 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

« Ответ #50 : 03-10-2010 17:42 » 

jur, да на самом деле все точно так же. что CyAPI что просто API... ничего там сверх естественного придумать нельзя) даже особо ничего скрыть не получится... ну чуть побольше if-ов с проверками, но по мне лучше сразу разбираться с первоисточником, чем с чужими "фичами", которые ни в коем случае не "баги")))

Полностью согласен. Тока я пока напрямую с драйверами работать не умею :-)

А с этой, не к ночи будь помянутой, FTDI я тоже работал. Еще перед Сайпресом. Переписывался с ними, доказывал, что то, что они называют фичей, - суть элементарный баг :-) (Они это потом в кремнии исправили. Уже запамятовал, о чем там речь, но что-то с их левым подходом, что импульс считывания нормальный, инверсный, а записи - нет. Там по падающему фронту положительного импульса, кажись. В общем, не помню уже.) Особо удручил меня их глюкодром под названием "Драйвер для COM-порта", который по-продвинутее.  После целой вереницы BSOD-ов я перешел на Сайпрес, чему рад по сей день :-)

Правда, надо отдать должное, с микросхемой FTDI (я работал с FT245AM, потом с FT245BM) организовать простейшее взаимодействие своего железа по USB максимально просто.
Записан

MPEG-4 - в массы!
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #51 : 03-10-2010 17:59 » 

jur, так у нас ее и начали тыкать куда непопадя, из за этой простоты.
говорят они новую версию на полной скорости освоили.
кстати, напрямую -  это всего лишь банальный ReadFile/WriteFile ну или DeviceIoControl, что суть то же самое но с большими возможностями.
а "продвинутый" я делал. вроде проблем ни одной не было. там правда особо ничего не требовалось
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Bulat
Участник

ru
Offline Offline

« Ответ #52 : 07-10-2010 09:48 » 

Реализовал прием следующим образом:
Код:
UINT ReceiveThread( LPVOID pParam )
{
unsigned char buf_r[queuesize][60];
unsigned char buffer_r[queuesize][60];
DWORD nEvent;

        //Заполняю массив ивентов
for(int i=0; i<queuesize; i++)
{
hEvent_Read[i] = CreateEvent(
NULL,    // default security attribute
FALSE,    // manual-reset event
TRUE,    // initial state = signaled
NULL);   // unnamed event object

// set up overlapped structure fields
gOverLapped_Read[i].Offset     = 0;
gOverLapped_Read[i].OffsetHigh = 0;
gOverLapped_Read[i].hEvent     = hEvent_Read[i];
}


stop_r = 0;
RealRead = 0;
        //Запуск оснновного цикла приема данных
while(stop_r == 0)
{
DWORD dwError;

int queue_rem = queuesize; //Остаток очереди
                //Создаем очередь запросов на чтение
for(int i=0; i<queuesize; i++)
{
Success = ReadFile(PipeIn, &buf_r[i], 60, &RealRead, &gOverLapped_Read[i]);
if (Success) { for(int j=0; j<60; j++) {buffer_r[i][j] = buf_r[i][j];} CloseHandle(hEvent_Read[i]); queue_rem--;} //Данные приняты

}
//Разбираем остаток очереди запросов, которые не были обработаны сразу после выполнения readfile
for(int i=0; i<queue_rem; i++)
{
nEvent = WaitForMultipleObjects(queuesize, hEvent_Read, FALSE, INFINITE);
if(nEvent == WAIT_FAILED);
if(nEvent <= queuesize)
{
recv_index = nEvent;
Success = GetOverlappedResult(PipeIn,&gOverLapped_Read[i],&RealRead, FALSE);
if (!Success)
{
// deal with the error code
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we have reached the end of
// the file during asynchronous
// operation
}
 
// deal with other error cases
}   //end switch (dwError = GetLastError())
}//if (!Success)
else for(int j=0; j<60; j++) buffer_r[i][j] = buf_r[i][j]; //Данные приняты
if(RealRead) {RD_spd += RealRead; RealRead=0;}
}
}

                for(int i=0; i<queuesize; i++)
{
                     // разбираем buffer_r[i][j]
                }
               

}//while(stop_r == 0)

return 0;
AfxEndThread(0, TRUE);
}


Но это особо не помогло. Более того, при увеличении queuesize в 2-3 раза кол-во пропусков растет.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #53 : 07-10-2010 10:28 » 

CreateEvent( ......, TRUE, ...)    // initial state = signaled  
  - почему?Не понял?

if (Success) { for(int j=0; j<60; j++) {buffer_r[j] = buf_r[j];} CloseHandle(hEvent_Read); queue_rem--;} //Данные приняты
  - логически не верно. если один из запросов вернет TRUE (навряд ли, но мало ли) то вы удалите один хендел из массива (в произвольном месте), а после цикла передадите полный массив хендлов событий на WFSO. и будет ошибка.

if(nEvent == WAIT_FAILED); - классное выражение) особенно точка с запятой после if-a) и работает?

WFSO с INFINITE ожиданием - это не красиво. и никакой while(stop_r == 0) вам не поможет.

nEvent = WaitForMultipleObjects(...)
Success = GetOverlappedResult(PipeIn,&gOverLapped_Read,&RealRead, FALSE);
- вообще говоря тоже ошибка... позже -  вторая такая же. хотя в этом случае может и сработать. как повезет... пишите внимательнее.

ключевой вопрос - ка вы судите о наличии пропусков?
« Последнее редактирование: 07-10-2010 12:35 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Bulat
Участник

ru
Offline Offline

« Ответ #54 : 09-10-2010 08:55 » 

ключевой вопрос - ка вы судите о наличии пропусков?
Создаю огромный массив типа char, размером 5000000 и записываю туда принятые в потоке чтения пакеты. По останову потока начинаю разбирать этот массив. Некий девайс шлет моему девайсу по Arinc429-каналу поток данных от 1 до 256, я эти данные регистрирую и после останова анализирую на нарушение последовательности от 1 до 256, то есть 1,2,..256,1,2,..256,..

    Мне удалось серьезно снизить количество пропусков при приеме за счет того, что из потока чтения я убрал разбор принятых пакетов, а лишь записываю эти пакеты в мой огромный буфер.
Код:

unsigned char buffer_r[5000000][60];

UINT ReceiveThread( LPVOID pParam )
{

unsigned char buf_r[queuesize][60];
DWORD dwError;

for(int i=0; i<queuesize; i++)
{
hEvent_Read[i] = CreateEvent(
NULL,    // default security attribute
FALSE,    // auto-reset event
FALSE,    // initial state = nonsignaled
NULL);   // unnamed event object
// set up overlapped structure fields
gOverLapped_Read[i].Offset     = 0;
gOverLapped_Read[i].OffsetHigh = 0;
gOverLapped_Read[i].hEvent     = hEvent_Read[i];
}

stop_r = 0;
RealRead = 0;

while(stop_r == 0)
{
for(int i=0; i<queuesize; i++)
{
Success = ReadFile(PipeIn, &buf_r[i], 60, &RealRead, &gOverLapped_Read[i]);
}

for(int i=0; i<queuesize; i++)
{
nEvent = WaitForMultipleObjects(queuesize, hEvent_Read, FALSE, 100);
if(nEvent <= queuesize)
{
recv_index = nEvent;
Success = GetOverlappedResult(PipeIn,&gOverLapped_Read[i],&RealRead, FALSE);
if (!Success)
{
// deal with the error code
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we have reached the end of
// the file during asynchronous
// operation
}
 
// deal with other error cases
}   //end switch (dwError = GetLastError())
}//if (!Success)
                        else {
                    for(int j=0; j<RealRead; j++) buffer_r[i_rcv][j] = buf_r[i][j]; //Переписываю принятый пакет в буфер.
                    i_rcv++;
                        } //Данные приняты
                        if(RealRead) {RD_spd += RealRead; RealRead=0;}
}
}

}//while(stop_r == 0)

return 0;
AfxEndThread(0, TRUE);
}
Так вот, если я определю размер очереди queuesize равным 1, то есть очереди не будет, то количество пропусков такое же, как и при размере очереди 15, а если значительно увеличивать размер очереди (queuesize=50), то кол-во пропусков увеличивается.
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #55 : 09-10-2010 09:59 » 

Цитата
"Так вот, если я определю размер очереди queuesize равным 1, то есть очереди не будет, то количество пропусков такое же, как и при размере очереди 15, а если значительно увеличивать размер очереди (queuesize=50), то кол-во пропусков увеличивается."
- это говорит что у вас до сих пор алгоритм неверно построен. вы разделили его на два цикла в первом только отсылаете запросы, во втором ждете полного опустошения очереди запросов.
при правильно построенном алгоритме - очередь не должна полностью опустошаться.

ЗЫ кроме того... для копирования памяти обычно пользуются memcpy(). так быстрее

PPS вы должны отправлять новый запрос ReadFile, как только придет подтверждение чтения хотя бы одного из очереди... не дожидаясь пока придут все.
« Последнее редактирование: 09-10-2010 10:06 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

« Ответ #56 : 10-10-2010 12:36 » 

Так вот, если я определю размер очереди queuesize равным 1, то есть очереди не будет, то количество пропусков такое же, как и при размере очереди 15, а если значительно увеличивать размер очереди (queuesize=50), то кол-во пропусков увеличивается.

Уфф... Опять все неправильно... Я ведь несколько раз говорил, что запускать все запросы нужно до входа в цикл! Поэтому вот этот код:

Код:
for(int i=0; i<queuesize; i++)
{
    Success = ReadFile(PipeIn, &buf_r[i], 60, &RealRead, &gOverLapped_Read[i]);           
}

нужно поставить не внутрь цикла "while(stop_r == 0)", а перед ним. А в цикле просыпаться по ивенту одного единственного - очередного - выполненного считывания, т.е. очередной принятой порции данных. Получить эту порцию данных и снова запустить только вот это одно только что выполненное чтение! Т.о. в очереди всегда будет несколько ожидающих запросов, что и позволит принимать данные и быстро, и без потерь.

А то, что сделано у вас - это какое-то нагромождение непонятного кода, который работает без всякой очереди с разделением времени. Естественно, что queuesize равная 1 будет работать точно так же неправильно, как и queuesize = 15.

Чтобы очередь нормально работала (т.е. чтобы она вообще была) нужно просто реализовать алгоритм, который я подробно разжевал в посте #48.
Записан

MPEG-4 - в массы!
Bulat
Участник

ru
Offline Offline

« Ответ #57 : 14-10-2010 05:05 » 

Вроде получилось) Во всяком случае заметно влияние размера очереди queuesize. Правда больше 60 запросов не получается очередь сделать, не работает. Если приложение работает в режиме реального времени и отключен вывод по таймеру  принимаемого потока данных, то на медленных машинах пропусков нет, но дело в том, что я очередь сделал для приема, а для передачи еще не делал очереди. Насколько я понимаю, для передачи примерно такой же алгоритм, так что надеюсь, что решу эту проблему.
Код:
UINT ReceiveThread( LPVOID pParam )
{
unsigned char buf_r[queuesize][60];

for(int i=0; i<queuesize; i++)
{
hEvent_Read[i] = CreateEvent(
NULL,    // default security attribute
FALSE,    // auto-reset event
FALSE,    // initial state = nonsignaled
NULL);   // unnamed event object



// set up overlapped structure fields
gOverLapped_Read[i].Offset     = 0;
gOverLapped_Read[i].OffsetHigh = 0;
gOverLapped_Read[i].hEvent     = hEvent_Read[i];
}

stop_r = 0;
RealRead = 0;

for(int i=0; i<queuesize; i++) ReadFile(PipeIn, &buf_r[i], 60, &RealRead, &gOverLapped_Read[i]);

while(stop_r == 0)
{

nEvent = WaitForMultipleObjects(queuesize, hEvent_Read, FALSE, 100);
if(nEvent <= queuesize)
{
recv_index = nEvent;
Success = GetOverlappedResult(PipeIn,&gOverLapped_Read[recv_index],&RealRead, FALSE);
if (!Success)
{
// deal with the error code
switch (dwError = GetLastError())
{
case ERROR_HANDLE_EOF:
{
// we have reached the end of
// the file during asynchronous
// operation

// deal with other error cases
}   //end switch (dwError = GetLastError())
}//if (!Success)
else {
memcpy(buffer_r[i_rcv],buf_r[recv_index],60);
i_rcv++;

if(RealRead) {RD_spd += RealRead; RealRead=0;}
ReadFile(PipeIn, &buf_r[recv_index], 60, &RealRead, &gOverLapped_Read[recv_index]);
} //Данные приняты
}

}//while(stop_r == 0)

return 0;
AfxEndThread(0, TRUE);
}

Спасибо большое за помощь!
Записан
Ochkarik
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #58 : 14-10-2010 11:56 » 

похоже на правду)

PS кстати очень рекомендую всегда делать проверку ВСЕХ возможных возвращаемых значений на предмет ошибки.
это хороший стиль программирования, и всегда облегчает поиск багов)
а по нештатным возвращаемым значениям делать хотя бы MessageBox() или  assert().
это дает гарантию отлова таких ситуаций на момент поступления, а не по факту того: что непонятно где, непонятно что -  заработало криво.
Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
jur
Помогающий

lt
Offline Offline

« Ответ #59 : 14-10-2010 20:30 » 

Вроде получилось)
...
Спасибо большое за помощь!

Ничуть не сомневался, что получится Улыбаюсь Успехов!
Записан

MPEG-4 - в массы!
Страниц: 1 [2] 3  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines