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

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

ru
Offline Offline

« : 17-09-2009 19:41 » 

Здравствуйте.
Мой драйвер для PCI Bus-master устройства использует при передаче данных в компьютер непрерывный буфер небольшого размера.(64 килобайта) Выделяю его с помощью функции AllocateCommonBuffer после получения указателя на DMA адаптер через IoGetDmaAdapter. Устройство достаточно мощное в плане DMA и может передавать до 32 блоков данных разного размера (кратного 1024 байтам) за один цикл пересылки содержимого его внутреннего буфера (который составляет 64 Мбайт). Я отображаю буфер на пользовательское адресное пространство и обрабатываю полученные данные. Не так давно задумался над повышением размера буфера. Сразу пришла мысль использовать Scatter-Gather механизм, но не совсем так как это делается через GetScatterGatherList или BuildScatterGatherList. Ведь если я правильно понимаю, то система сама единовременно выделяет Scatter - Gather лист и вызывает Dpc процедуру в которой этот лист можно использовать а затем его нужно освобождать. Мне бы хотелось сделать такую вот вещь - взять и однократно сформировать такой Scatter - Gather List для некоторого буфера достаточно большого размера, и сделать его виртуально непрерывным.(получить MDL через функцию точно не помню сейчас как называется из объекта адаптера DMA, получить виртуальный адрес этого MDL). Затем так же отобразить этот виртуальный адрес на пользовательское пространство памяти и работать как обычно. Одно решение напрашивается само собой - это сделать подобный список вручную через вызовы AllocateCommonBuffer или любые другие функции выделения физически нерпрерывных участков памяти. Но может быть можно сделать это как-то через CalculateScatterGatherList/BuildScatterGatherList, и в функции Dpc после вызова BuildScatterGatherList не инициировать никаких операций DMA а просто запомнить полученный Scatter-Gather List? Я попытался у себя в драйвере выделить буфер из обычного пула неподкачиваемой памяти, затем сформировать MDL и вызывать CalculateScatterGatherList/BuildScatterGatherList для этого буфера и MDL но вызов привел к неудаче со статусом STATUS_INSUFFICIENT_RESOURCES. Наверное я что то не понимаю в механизме Windows для Scatter - Gather или я что то не так сделал ? Подскажите пожалуйста что может быть не так. Может быть кто то уже пытался использовать этот механизм подобным образом ?
Записан
Ochkarik
Модератор

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

« Ответ #1 : 17-09-2009 20:48 » 

хм. время позднее сразу не осознаю...
но я так понимал, что список адресов этих непрерывных блоков надо загружать в плату? правда сам с таким не занимался...
а вообще непрерывно можно и под 8-16Мбайт выделить. без всяких ухищрений. теоретически до 64. см ниже.
« Последнее редактирование: 18-09-2009 22:11 от Ochkarik » Записан

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

ru
Offline Offline

« Ответ #2 : 18-09-2009 09:36 » 

Да совершенно верно. Список адресов и длин загружается в плату один раз что очень удобно. Просто хочется заложиться на будущее и уметь формировать виртуально непрерывный буфер, который тем не менее будет физически наподобие Scatter-Gather List выглядеть. Я там немножко разогнался в предыдущем посте) Получить Mdl на сформированный Scatter - Gather List можно только если есть Mdl из которого был сделан этот Scatter - Gather List) А у меня пока не получается для обычного буфера из NonPaged Pool сформировать Scatter-Gather List) Так что объединять память придется другим путем пока что если не разберусь с тем как получить SG List)
Записан
Ochkarik
Модератор

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

« Ответ #3 : 18-09-2009 17:13 » 

не совсем понял.... при работе через AllocateAdapterChannel->AllocateCommonBuffer() - разве нельзя получить MDL всего региона? или в чем проблема?
я, честно говоря, слабо представляю в какой последовательности вызываются функции при размещении буфера для DMA операций со ScatterGatherList...
очень уж запутанно написано... а экспериментировать не на чем было)
может проясните ситуацию (можно в виде кода))) буду очень признателен)
Записан

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

ru
Offline Offline

« Ответ #4 : 18-09-2009 19:18 » 

Прошу прощения, действительно запутанно написал.) Наверное, правильно будет действительно начать с сути. Тем более, что проблему я, вроде бы, победил. Как я уже написал ранее, сейчас я выделяю один непрерывный буфер в NonPaged Pool с помощью как раз функции AllocateCommonBuffer. Она возвращает как физический адрес непрерывного в памяти буфера, так и его виртуальный адрес для доступа из режима ядра. Все, что остается сделать мне, это спроецировать его в адресное пространство пользовательского процесса, от контекста которого поступает запрос в драйвер. Я сегодня попытался выделять непрерывные участки до 64 Мбайт включительно. Вы оказались совершенно правы - проблем не возникает с выделением таких объемов памяти. И все ПО при этом работает вполне нормально. Но все же при увеличении объемов передачи хотелось бы иметь возможность использовать большой, но не обязательно непрерывный буфер. Ведь выделить несколько кусков не слишком большого объема, по идее, системе будет куда легче и менее критично. Это суть того, что я хочу изменить в драйвере.)
Один из способов предлагали Вы в теме о "склейке" нескольких участков памяти с помощью функций выделения региона виртуальных адресов и затем проецировании на этот регион всех кусков через их MDL списки. Способ мне нравится, и я им воспользовался бы непременно, но все время вертится одна шальная мысль. А что если заставить систему саму сформировать мне этот буфер из кусков? И сделать это, похоже, можно именно через Scatter-Gather. Улыбаюсь
К сожалению, код остался на работе... ( Попытаюсь внятно описать то, до чего удалось докопаться по поводу Scatter - Gather, возможно будет что то полезное. Во время получения объекта адаптера DMA через IoGetDmaAdapter нужно правильно указать в структуре описания свойств устройства с точки зрения DMA максимальную длину транслируемых данных. Затем есть два пути:
Первый - это использовать GetScatterGatherList. Это впервые появилось в Windows 2000 и соответствует версии DMA 1. Чаще всего операции DMA, как посчитали в Microsoft, используются при получении данных в IRP_MJ_READ и посылке данных через IRP_MJ_WRITE. В составе IRP пакета в этом случае передается MDL пользовательского буфера. Именно описание этого буфера (то есть его MDL, начальный виртуальный адрес, и длину ) нужно передавать в функцию GetScatterGatherList. Дальше система делает следующее. Она ждет пока освободится данный адаптер DMA, что, наверное, актуально если используется разделяемый ресурс, типа системного контроллера DMA. В случае специального устройства не думаю, что это важно. А затем вызывает функцию типа AdapterControlProcedure, определенную в драйвере, и адрес которой передается в вызов GetScatterGatherList. То есть вызывается Dpc процедура.  В нее передается помимо прочих параметров (таких как определенный пользователем Context, указатель на адаптер и прочее ), как раз указатель на сформированный системой Scatter-Gather List. Предполагается что именно в этой Dpc процедуре программист и инициирует устройство для обмена через DMA используя записи о выделенных системой физически непрерывных регионах памяти в параметре Scatter-Gather List. То есть между вызовами GetScatterGatherList и Dpc процедуры в драйвере система сама формирует Scatter - Gather List по переданному программистом первоначальному MDL и дополнительным параметрам (длина и начальный виртуальный адрес буфера для которого создан MDL). По окончании DMA операции система вроде бы что то там освобождает. Но я так и не смог уяснить, зачем тогда программисту вызывать PutScatterGatherList, чтобы освободить ресурсы, если ситема сама освобождает память.(
Второй путь - это вместо GetScatterGatherList использовать пару функций CalculateScatterGatherList/BuildScatterGatherList. Это актуально уже для Windows XP и выше с версией DMA 2. Насколько я понял, основное отличие заключается в том, что программист может сам выделить требуемый объем памяти под Scatter-Gather List в своем драйвере, а уже в вызове BuildScatterGatherList передать адрес своего буфера чтобы система в нем сформировала список физически непрерывных регионов. В остальном работа GetScatterGatherList и BuildScatterGatherList одинакова. Необходимый объем памяти под Scatter-Gather List вычисляет функция CalculateScatterGatherList. Причем, в нее можно даже не передавать MDL буфера пользователя, а только лишь требуемую длину, при этом система вычислит максимально возможный объем списка.
Надеюсь что в этот раз получилось чуть менее путано.
Я решил пойти как раз вторым путем, и мне удалось сегодня получить список. ) Я ничего не делал в Dpc процедуре, просто запомнил список у себя в драйвере и все. А для эксперимента я выделял буфер в неподкачиваемой памяти прямо в драйвере и по нему получал Scatter-Gather List.

Прошу прощения за слишком длинный пост в очередной раз. Улыбаюсь Я как раз ошибся изначально при получении адаптера DMA, указав слишком маленькую максимальную длину транслируемых данных. Когда исправил ошибку, все сразу заработало. Если Вам будет интересно, я более подробно приведу описание эксперимента с участками кода (не хочу дальше загружать форум). Но быть может, и так все вполне понятно. Спасибо за информацию о максимально возможных объемах непрерывных буферов - очень помогло реально!
« Последнее редактирование: 19-09-2009 16:29 от Sel » Записан
Ochkarik
Модератор

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

« Ответ #5 : 18-09-2009 21:40 » 

Наоборот, спасибо за развернутый ответ) а про "запутанно написано" я имел в виду - DDK)
пара комментариев:
Насчет ограничения на максимальный размер непрерывно выделяемой памяти - где то я читал (но точно не помню), что было ограничение на максимальный размер NonPaged pool - 64Мбайта. Сейчас уже не уверен в этом полностью, возможно это не так.
выделение памяти. В процессе работы память все таки дефрагментируется участками NonPagedPool, поэтому есть вероятность что в какой то момент - система не сможет найти запрошенное количество непрерывной памяти. с этим я реально сталкивался, принудительно выделяя по 8Мбайт и освобождая ее. в конце концов стал выделять при старте системы и больше не освобождать.

поправка по максимальному размеру памяти OC NonPagedPool Limit (http://msdn.microsoft.com/en-us/library/aa366778(VS.85).aspx)
для 32 битных Windows Vista: ограничивается 2Gb или через реестр. "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\"
для 32битных Windows XP/2000:  256 MB, or 128 MB with 4GT.
для 64 битных версий Windows Vista:  40% of RAM up to a maximum of 128 GB.
для 64 битных Windows Server 2003 and Windows XP:  75% of RAM up to a maximum of 128 GB


« Последнее редактирование: 18-09-2009 22:16 от Ochkarik » Записан

RTFM уже хоть раз наконец!  RTFM :[ ну или хотя бы STFW...
Ochkarik
Модератор

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

« Ответ #6 : 18-09-2009 22:09 » 

И попробую формулировать то что я понял. может быть поправите меня немного. "Картина мира" если угодно)))

1. работа с DMA в рамках вызова IRP_MJ_READ/ IRP_MJ_WRITE/и т.д.
итак, если я правильно понял, то основное назначение Scatter-Gather List - поддержка запросов IRP_MJ_READ/ IRP_MJ_WRITE и т.п., причем с параметром METHOD_IN_DIRECT/METHOD_OUT_DIRECT.
Это означает что память для операции выделяет пользователь. Cледовательно она может быть кеширована процессором (отсюда требование KeFlushIoBuffers) и она точно из PagedPool(то есть может быть свопирована в настоящий момент).
Таким образом, для возможности использования ее для DMA - ее надо залочить в памяти(а эта операция не может быть выполнена моментально- т.к. возможно потребуется дисковая операция - отсюда возникает требование отложенной DPC). Кроме того, она явно выделена не непрерывно - следовательно надо согласовать число физических фрагментов региона где она расположена с максимальным размером Scatter-Gather List поддерживаемым устройством, т.е. с количеством NumberOfMapRegisters при помощи CalculateScatterGatherList, и быть готовым к тому, что физически она может быть слишком дефрагментирована для использования ее для DMA нашей платы. В последнем случае, рекомендуют разбивать один запрос на несколько при помощи MapTransfer.(здесь это делается автоматом?)

Далее, вызывается собственно GetScatterGatherList. Она содержит вызовы AllocateAdapterChannel и MapTransfer. То есть, она сама разбивает выделенный регион на необходимое число запросов(при помощи MapTransfer). На каждый из них вызывает пользовательскую PDRIVER_LIST_CONTROL  ExecutionRoutine, с размером PSCATTER_GATHER_LIST  ScatterGather, укладывающимся в рамки NumberOfMapRegisters.
На каждый вызов пользовательской ExecutionRoutine, пользователь конфигурирует плату на выполнение DMA операций с заданным ScatterGatherList, после чего ждет ее завершения(например прерыванием от платы).
дождавшись завершения DMA операции (например по ISR от платы) вызывает PutScatterGatherList, чем сообщает системе, что текущая транзакция по DMA закончена, и система может готовить следующий участок  из MapTransfer, для новой транзакции.
Далее снова вызывается пользовательская ExecutionRoutine, для следующего блока.
кстати, PutScatterGatherList помимо перехода к следующей транзакции  - видимо должна разлочивать фрагмент пользовательского буфера.
источники:
http://msdn.microsoft.com/en-us/library/cc264566.aspx
http://msdn.microsoft.com/en-us/library/cc264560.aspx


2. работа DMA в рамках запросов не связанных с IRP.
Принцип тот же, однако некоторые действия пользователь должен совершить явно.
  Разместить память и сформировать для нее MDL.
  Выполнить KeFlushIoBuffers.
  Вызвать AllocateAdapterChannel, с заданием максимально доступного для платы  NumberOfMapRegisters.
  Проверить дефрагментацию всего блока при помощи CalculateScatterGatherList и выделить память для ScatterGatherList.
  Вызвать MmGetMdlVirtualAddress, для нахождения начального адреса региона памяти для текущей транзакции (он считается итеративно на каждом запросе, если их несколько).
  Вызвать MapTransfer, для разбивки участка памяти с дефрагментацией превышающей NumberOfMapRegisters. в результате получит ScatterGatherList, содержащий фрагмент исходного буфера?
  Выполнить BuildScatterGatherList с параметром пользовательской ExecutionRoutine и задав ScatterGatherList удовлетворяющий NumberOfMapRegisters (либо весь буфер, либо фрагмент подготовленный после MapTransfer).
в ExecutionRoutine- сконфигурить плату на выполнение DMA транзакции.
  Дождаться завершения транзакции (плата сигнализирует прерыванием, в котором пользователь вызовет  в своей DpcForIsr функцию FlushAdapterBuffers, для завершения текущей транзакции. после чего (там же?) опять вызовет MapTransfer и BuildScatterGatherList  для начала транзакции по следующему участка буфера?
по завершению всего естественно FreeAdapterChannel .
Источники:
http://msdn.microsoft.com/en-us/library/cc264576.aspx
http://msdn.microsoft.com/en-us/library/cc264546.aspx


уффф!... как-то так получается? пытался хотя бы примерно обобщить... может быть прокомментируете? наверняка я чего то до конца не понял. и кстати, если выложите примеры своего кода - думаю это многим может пригодится)
PS сообщение - потом буду править по ходу разбирательств) изложенное может содержать массу неточностей и ошибок!
PPS у меня ощущение что я типы DMA тут напутал... что такое MasterDMA я понимаю. а вот что за System DMA?
« Последнее редактирование: 19-09-2009 09:21 от Ochkarik » Записан

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

ru
Offline Offline

« Ответ #7 : 19-09-2009 10:40 » 

Спасибо Вам еще раз за описание ограничений по выделению памяти! Учту это обязательно!
Примеры кода, чтобы ничего не наврать, точно смогу выложить в понедельник вечером после работы. Сразу же уточню - я в большей степени при разбирательствах с DMA и Scatter-Gather опирался на соответствующую главу у Walter Oney из книжки "Programming WDM". Вы совершенно правы - в DDK все очень неоднозначно описано.) И только почитав Oney, я, вроде бы, начал понимать какую-то суть организации DMA). Тут же следуя терминологии из этой книжки - Bus Master DMA это когда сама плата обладает всей необходимой электроникой для захвата шины и трансляции данных. Если же для передачи необходимо программировать системный контроллер DMA (для современных устройств думаю чистейший анахронизм), то это называется, вроде бы, Slave Dma с использованием System DMA Controller. Я просто жаргонно назвал это System DMA)
Вот выдержка из Oney по этому поводу:

How you perform a DMA transfer depends on several factors:

If your device has bus-mastering capability, it has the necessary electronics to access main memory if you tell it a few basic facts, such as where to start, how many units of data to transfer, whether you're performing an input or an output operation, and so on. You'll consult with your hardware designers to sort out these details, or else you'll be working from a specification that tells you what to do at the hardware level.

A device with scatter/gather capability can transfer large blocks of data to or from discontiguous areas of physical memory. Using scatter/gather is advantageous for software because it eliminates the need to acquire large blocks of contiguous page frames. Pages can simply be locked wherever they're found in physical memory, and the device can be told where they are.

If your device is not a bus master, you'll be using the system DMA controller on the motherboard of the computer. This style of DMA is sometimes called slave DMA. The system DMA controller associated with the ISA bus has some limitations on what physical memory it can access and how large a transfer it can perform without reprogramming. The controller for an EISA bus lacks these limits. You won't have to know—at least, not in Windows 2000—which type of bus your hardware plugs in to because the operating system is able to take account of these different restrictions automatically.

Ordinarily, DMA operations involve programming hardware map registers or copying data either before or after the operation. If your device needs to read or write data continuously, you don't want to do either of these steps for each I/O request—they might slow down processing too much to be acceptable in your particular situation. You can, therefore, allocate what's known as a common buffer that your driver and your device can both simultaneously access at any time.

То есть, если устройство не поддерживает Bus Master, то нужно программировать system controller.

А вот насчет стратегий обмена по DMA вы совершенно верно описали первую из них, когда выделяется канал DMA с помощь AllocateAdapterChannel.


« Последнее редактирование: 19-09-2009 16:21 от Sel » Записан
SergeiS
Участник

ru
Offline Offline

« Ответ #8 : 19-09-2009 10:47 » 

Вот как это описывает Oney:
To initiate an I/O operation, your StartIo routine first has to reserve the adapter object by calling the object's AllocateAdapterChannel routine. One of the arguments to AllocateAdapterChannel is the address of an adapter control routine that the I/O Manager will call when the reservation has been accomplished. Here's an example of code you would use to prepare and execute the call to AllocateAdapterChannel:



1













2






3




4








5

 typedef struct _DEVICE_EXTENSION {
  ...
  PADAPTER_OBJECT AdapterObject; // device's adapter object
  ULONG nMapRegisters; // max # map registers
  ULONG nMapRegistersAllocated; // # allocated for this xfer
  ULONG numxfer;       // # bytes transferred so far
  ULONG xfer;          // # bytes to transfer during this stage
  ULONG nbytes;        // # bytes remaining to transfer
  PVOID vaddr;         // virtual address for current stage
  PVOID regbase;       // map register base for this stage
  ...
  } DEVICE_EXTENSION, *PDEVICE_EXTENSION;

VOID StartIo(PDEVICE_OBJECT fdo, PIRP Irp)
  {
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
  if (!NT_SUCCESS(status))
    {
    CompleteRequest(Irp, status, 0);
    return;
    }

  PMDL mdl = Irp->MdlAddress;
  pdx->numxfer = 0;
  pdx->xfer = pdx->nbytes = MmGetMdlByteCount(mdl);
  pdx->vaddr = MmGetMdlVirtualAddress(mdl);

  ULONG nregs = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pdx->vaddr,
    pdx->nbytes);
  if (nregs > pdx->nMapRegisters)
    {
    nregs = pdx->nMapRegisters;
    pdx->xfer = nregs * PAGE_SIZE - MmGetMdlByteOffset(mdl);
    }
  pdx->nMapRegistersAllocated = nregs;

  status = (*pdx->AdapterObject->DmaOperations
    ->AllocateAdapterChannel)(pdx->AdapterObject, fdo, nregs,
    (PDRIVER_CONTROL) AdapterControl, pdx);
  if (!NT_SUCCESS(status))
    {
    IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
    CompleteRequest(Irp, status, 0);
    StartNextPacket(&pdx->dqReadWrite, fdo);
    }
  }

 



Your device extension needs several fields related to DMA transfers. The comments indicate the uses for these fields.


This is the appropriate time to claim the remove lock to forestall PnP removal events during the pendency of the I/O operation. The balancing call to IoReleaseRemoveLock occurs in the DPC routine that ultimately completes this request.


These few statements initialize fields in the device extension for the first stage of the transfer.


Here, we calculate how many map registers we'll ask the system to reserve for our use during this transfer. We begin by calculating the number required for the whole transfer. The ADDRESS_AND_SIZE_TO_SPAN_PAGES macro takes into account that the buffer might span a page boundary. The number we end up with might, however, exceed the maximum allowed us by the original call to IoGetDmaAdapter. In that case, we need to perform the transfer in multiple stages. We therefore scale back the first stage so as to use only the allowable number of map registers. We also need to remember how many map registers we're allocating (in the nMapRegistersAllocated field of the device extension) so that we can release exactly the right number later on.


In this call to AllocateAdapterChannel, we specify the address of the adapter object, the address of our own device object, the calculated number of map registers, and the address of our adapter control procedure. The last argument (pdx) is a context parameter for the adapter control procedure.

In general, several devices can share a single adapter object. Adapter object sharing happens in real life only when you rely on the system DMA controller; bus-master devices own dedicated adapter objects. But, since you don't need to know how the system decides when to create adapter objects, you shouldn't make any assumptions about it. In general, then, the adapter object might be busy when you call AllocateAdapterChannel, and your request might therefore be put into a queue until the adapter object becomes available. Also, all DMA devices on the computer share a set of map registers. Further delay can ensue until the requested number of registers becomes available. Both of these delays occur inside AllocateAdapterChannel, which calls your adapter control procedure when the adapter object and all the map registers you asked for are available.

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

ru
Offline Offline

« Ответ #9 : 19-09-2009 10:54 » 

Извините, нумерация шагов съехала. Жаль

Честно говоря, я так и не могу до конца понять роль Map регистров. Я понимаю, что это фактически описание пропускной способности DMA операции. Но для современных плат мне кажется актуальнее количество неподкачиваемой памяти в системе. Впрочем, похоже, что это где-то на грани перетекания из одного в другое. Что-то на философию Канта потянуло. Улыбаюсь

Вот еще очень интересное замечание:

Even though a PCI bus-mastering device owns its own adapter object, if the device doesn't have scatter/gather capability, it requires the use of map registers. On CPUs like Alpha that have map registers, AllocateAdapterChannel will reserve them for your use. On CPUs like Intel that don't have map registers, AllocateAdapterChannel will reserve use of a software surrogate, such as a contiguous area of physical memory.

What Gets Queued in AllocateAdapterChannel?

The object that AllocateAdapterChannel puts into queues to wait for the adapter object or the necessary number of map registers is your device object. Some device architectures allow you to perform more than one DMA transfer simultaneously. Since you can put only one device object into an adapter object queue at a time (without crashing the system, that is), you need to create dummy device objects to take advantage of that multiple-DMA capability.
« Последнее редактирование: 19-09-2009 16:30 от Sel » Записан
Ochkarik
Модератор

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

« Ответ #10 : 19-09-2009 10:57 » 

я пока тоже функции MapTransfer  не понял... она вроде бы логический адрес отдает. а логический адрес это для HAL.
NumberOfMapRegisters - для master DMA я так понял это количество непрерывных участков которое устройство может переварить за раз.
кроме того вроде бы есть ссылки о том что существуют платформы которые на уровне HAL сами умеют физические адреса на шине склеивать? если я понял правильно.
Записан

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

ru
Offline Offline

« Ответ #11 : 19-09-2009 11:02 » 

То есть, в первом абзаце Oney фактически признает, что Map Registers на платформе Intel - фактически Contiguous area Of physical Memory.) Вызов же Dpc, насколько следует из второго абзаца после вопроса, прежде всего актуален, когда устройство может выполнять несколько DMA операций сразу. Причем, приходится создавать dummy device object. Или я что-то тут не так перевел, или... очередные приколы в архитектуре от Microsoft.)

Да, похоже, вы совершенно правы - есть платформы, которые позволяют склеивать адреса!

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

Это я про Oney.)))

И про Microsoft.)

А далее вот что пишут)) :

As I've been discussing, AllocateAdapterChannel eventually calls your adapter control routine (at DISPATCH_LEVEL, just like your StartIo routine does). You have two tasks to accomplish. First, you should call the adapter object's MapTransfer routine to prepare the map registers and other system resources for the first stage of your I/O operation. In the case of a bus-mastering device, MapTransfer will return a logical address that represents the starting point for the first stage. This logical address might be the same as a CPU physical memory address, and it might not be. All you need to know about it is that it's the right address to program into your hardware. MapTransfer might also trim the length of your request to fit the map registers it's using, which is why you need to supply the address of the variable that contains the current stage length as an argument.

Your second task is to perform whatever device-dependent steps are required to inform your device of the physical address and to start the operation on your hardware:

1

2
3
4
5

6
7

 IO_ALLOCATION_ACTION AdapterControl(PDEVICE_OBJECT fdo,
  PIRP junk, PVOID regbase, PDEVICE_EXTENSION pdx)
  {
PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);
  PMDL mdl = Irp->MdlAddress;
  PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
BOOLEAN isread = stack->MajorFunction == IRP_MJ_READ;
pdx->regbase = regbase;
KeFlushIoBuffers(mdl, isread, TRUE);
PHYSICAL_ADDRESS address =
    (*pdx->AdapterObject->DmaOperations->MapTransfer)
    (pdx->AdapterObject, mdl, regbase, pdx->vaddr, &pdx->xfer,
    !isread);
...
return DeallocateObjectKeepRegisters;
« Последнее редактирование: 19-09-2009 16:37 от Sel » Записан
Ochkarik
Модератор

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

« Ответ #12 : 19-09-2009 11:07 » 

http://www.wl.unn.ru/LAB/Portals/0/IOArch3.pdf
выдержки из книги Внутреннее устройство Windows. Соломона-руссиновича кажется. там картинки есть) смотрю сейчас)
Записан

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

ru
Offline Offline

« Ответ #13 : 19-09-2009 11:10 » 

Вот общая схема подобной DMA транзакции.

О у меня есть эта книга в оригинале...)) Сильная вещь))

* MapTransfer.JPG (18.12 Кб - загружено 983 раз.)
« Последнее редактирование: 19-09-2009 16:39 от Sel » Записан
SergeiS
Участник

ru
Offline Offline

« Ответ #14 : 19-09-2009 11:14 » 

То есть далее после первого шага DMA пока все данные не будут переданы нужно возобновлять передачу:

An interrupt usually occurs shortly after you start the transfer, and the interrupt service routine usually requests a DPC to deal with completion of the first stage of the transfer. Your DPC routine would look something like this:




1



2


3


4


5

6















7


8

 VOID DpcForIsr(PKDPC Dpc, PDEVICE_OBJECT fdo, PIRP junk, PVOID Context)
  {
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);
  PMDL mdl = Irp->MdlAddress;
  BOOLEAN isread = IoGetCurrentIrpStackLocation(Irp)
    ->MajorFunction == IRP_MJ_READ;
  (*pdx->AdapterObject->DmaOperations->FlushAdapterBuffers)
    (pdx->AdapterObject, mdl, pdx->regbase, pdx->vaddr,
    pdx->xfer, !isread);
  pdx->nbytes -= pdx->xfer;
  pdx->numxfer += pdx->xfer;
  NTSTATUS status = STATUS_SUCCESS;
  ...
  if (pdx->nbytes && NT_SUCCESS(status))
    {
    pdx->vaddr = (PVOID) ((PUCHAR) pdx->vaddr + pdx->xfer);
    pdx->xfer = pdx->nbytes;
    ULONG nregs = ADDRESS_AND_SIZE_TO_SPAN_PAGES(pdx->vaddr,
      pdx->nbytes);
    if (nregs > pdx->nMapRegistersAllocated)
      {
      nregs = pdx->nMapRegistersAllocated;
      pdx->xfer = nregs * PAGE_SIZE;
      }
    PHYSICAL_ADDRESS address =
      (*pdx->AdapterObject->DmaOperations->MapTransfer)
      (pdx->AdapterObject, mdl, pdx->regbase, pdx->vaddr,
      pdx->xfer, !isread);
    ...
    }
  else
    {
    ULONG numxfer = pdx->numxfer;
    (*pdx->AdapterObject->DmaOperations->FreeMapRegisters)
      (pdx->AdapterObject, pdx->regbase,
      pdx->nMapRegistersAllocated);
    IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
    StartNextPacket(&pdx->dqReadWrite, fdo);
    CompleteRequest(Irp, status, numxfer);
    }
  }

 



When you use a DEVQUEUE for IRP queuing, you rely on the queue object to keep track of the current IRP.


The FlushAdapterBuffers routine handles the situation in which the transfer required use of intermediate buffers owned by the system. If you've done an input operation that spanned a page boundary, the input data is now sitting in an intermediate buffer and needs to be copied to the user-mode buffer.


Here, we update the residual and cumulative data counts after the transfer stage that just completed.


At this point, you determine whether the current stage of the transfer completed successfully or with an error. You might, for example, read a status port or inspect the results of a similar operation performed by your interrupt routine. In this example, I set the status variable to STATUS_SUCCESS with the expectation that you'd change it if you discovered an error here.


If the transfer hasn't finished yet, you need to program another stage. The first step in this process is to calculate the virtual address of the next portion of the user-mode buffer. Bear in mind that this calculation is merely working with a number—we're not actually trying to access memory by using this virtual address. Accessing the memory would be a bad idea, of course, because we're currently executing in an arbitrary thread context.


The next few statements are almost identical to the ones we performed in the first stage for StartIo and AdapterControl. The end result will be a logical address that can be programmed into your device. It might or might not correspond to a physical address as understood by the CPU. One slight wrinkle is that we're constrained to use only as many map registers as were allocated by the adapter control routine; StartIo saved that number in the nMapRegistersAllocated field of the device extension.


If the entire transfer is now complete, we need to release the map registers we've been using.


The remaining few statements in the DPC routine handle the mechanics of completing the IRP that got us here in the first place. We release the remove lock to balance the acquisition that we did inside StartIo.
Записан
Ochkarik
Модератор

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

« Ответ #15 : 19-09-2009 11:15 » 

Видимо для платформ типа Alfa, HAL умеет сам отображать физические адреса памяти для устройства в непрерывные. количество регистров такого HAL имеет ограничение и для разделения доступа требуется вызов MapTransfer... похоже на это?
PS у меня тоже в оригинале есть все) Руссинович, Они, Пенни орвик(пока не читал - про запас взял) ну и солдатов, Агуров и т.д.) библиотека однако)))  

Мне, пожалуй, пора... еще почитаю и подумаю))
« Последнее редактирование: 19-09-2009 16:39 от Sel » Записан

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

ru
Offline Offline

« Ответ #16 : 19-09-2009 11:20 » 

То есть, это все явно очень не быстро и довольно громоздко. Подходит, наверное, для длительных не очень критичных ко времени передач данных.

Всего доброго.))) Я же утолю свой словесный поток.))))

И вот, наверное, главная деталь всей пьесы)))))
Transfers Using Scatter/Gather Lists
If your hardware has scatter/gather support, the system has a much easier time doing DMA transfers to and from your device. The scatter/gather capability permits the device to perform a transfer involving pages that aren't contiguous in physical memory.

Your StartDevice routine creates its adapter object in just about the same way I've already discussed, except (of course) that you'll set the ScatterGather flag to TRUE.

The traditional method—that is, the method you would have used in previous versions of Windows NT—to program a DMA transfer involving scatter/gather functionality is practically identical to the packet-based example considered in the previous section, "Performing DMA Transfers." The only difference is that instead of making one call to MapTransfer for each stage of the transfer, you need to make multiple calls. Each call gives you the information you need for a single element in a scatter/gather list that contains a physical address and length. When you're done with the loop, you can send the scatter/gather list to your device by using some device-specific method, and you can then initiate the transfer.

« Последнее редактирование: 19-09-2009 16:41 от Sel » Записан
Ochkarik
Модератор

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

« Ответ #17 : 19-09-2009 11:22 » 

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

PS кстати, в 1394 например DPC после каждой транзакции вызывается. а транзакции там по 1кб могут быть... жесть?) однако по 20мбайт пролазит...
« Последнее редактирование: 19-09-2009 11:24 от Ochkarik » Записан

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

ru
Offline Offline

« Ответ #18 : 19-09-2009 11:27 » 

То есть, поначалу это было просто развитие пакетного обмена через DMA, того самого, который у Oney описан в предыдущих абзацах. И разница в том, что для каждой серии обмена данными нужен не один, а много вызовов MapTransfer в соответствии с количеством элементов в Scatter - Gather List) Здесь, честно говоря, я совсем потерял оптимизм.)))) И подумал, что лучше вообще не использовать получение адаптера, а выделить участок памяти и не мучиться.))))

Да жесть !!! Это точно)))) Причем, даже если пропускная способность до 20 Мб, все равно глаз режет и руки, когда нужно такое программировать.))))

Но вот спасительный абзац.))) И воля снова становится сильна.)))

Using GetScatterGatherList
Windows 2000 provides a shortcut to avoid the relatively cumbersome loop of calls to MapTransfer in the common case in which you can accomplish the entire transfer by using either no map registers or no more than the maximum number of map registers returned by IoGetDmaAdapter. The shortcut, which is illustrated in the SCATGATH sample on the companion disc, involves calling the GetScatterGatherList routine instead of AllocateAdapterChannel. Your StartIo routine looks like this:

VOID StartIo(PDEVICE_OBJECT fdo, PIRP Irp)
  {
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
  NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);
  if (!NT_SUCCESS(status))
    {
    CompleteRequest(Irp, status, 0);
    return;
    }
  PMDL mdl = Irp->MdlAddress;
  ULONG nbytes = MmGetMdlByteCount(mdl);
  PVOID vaddr = MmGetMdlVirtualAddress(mdl);
  BOOLEAN isread = stack->MajorFunction == IRP_MJ_READ;
  pdx->numxfer = 0;
  pdx->nbytes = nbytes;
  status = (*pdx->AdapterObject->DmaOperations->GetScatterGatherList)
    (pdx->AdapterObject, fdo, mdl, vaddr, nbytes,
    (PDRIVER_LIST_CONTROL) DmaExecutionRoutine, pdx, !isread);
  if (!NT_SUCCESS(status))
    {
    IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
    CompleteRequest(Irp, status, 0);
    StartNextPacket(&pdx->dqReadWrite, fdo);
    }
  }
 


The call to GetScatterGatherList, shown in bold in the previous code fragment, is the main difference between this StartIo routine and the one we looked at in the preceding section. GetScatterGatherList waits, if necessary, until you can be granted use of the adapter object and all the map registers you need. Then it builds a SCATTER_GATHER_LIST structure and passes it to the DmaExecutionRoutine. You can then program your device by using the physical addresses in the scatter/gather elements and initiate the transfer:


1
2

 VOID DmaExecutionRoutine(PDEVICE_OBJECT fdo, PIRP junk,
  PSCATTER_GATHER_LIST sglist, PDEVICE_EXTENSION pdx)
  {
  PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);
  pdx->sglist = sglist;
  ...
  }
 

You'll need the address of the scatter/gather list in the DPC routine, which will release it by calling PutScatterGatherList.


At this point, program your device to do a read or write using the address and length pairs in the scatter/gather list. If the list has more elements than your device can handle at one time, you'll need to perform the whole transfer in stages. If you can program a stage fairly quickly, I'd recommend adding logic to your interrupt service routine to initiate the additional stages. If you think about it, your DmaExecutionRoutine is probably going to be synchronizing with your ISR anyway to start the first stage, so this extra logic is probably not large. I programmed the SCATGATH sample with this idea in mind.

When the transfer finishes, call the adapter object's PutScatterGatherList to release the list and the adapter:

VOID DpcForIsr(PKDPC Dpc, PDEVICE_OBJECT fdo, PIRP junk, PVOID Context)
  {
  ...
  (*pdx->AdapterObject->DmaOperations->PutScatterGatherList)
    (pdx->AdapterObject, pdx->sglist, !isread);
  ...
  }
 

To decide whether you can use GetScatterGatherList, you need to be able to predict whether you'll meet the preconditions for its use. On an Intel 32-bit platform, scatter/gather devices on a PCI or EISA bus can be sure of not needing any map registers. Even on an ISA bus, you'll be allowed to request up to 16 map register surrogates (eight if you're also a bus-mastering device) unless physical memory is so tight that the I/O system can't allocate its intermediate I/O buffers. In that case, you wouldn't be able to do DMA using the traditional method either, so there'd be no point in worrying about it.

If you can't predict with certainty at the time you code your driver that you'll be able to use GetScatterGatherList, my advice is to just fall back on the traditional loop of MapTransfer calls. You'll need to put that code in place anyway to deal with cases in which GetScatterGatherList won't work, and having two pieces of logic in your driver is just unnecessary complication.

)
« Последнее редактирование: 19-09-2009 16:33 от Sel » Записан
SergeiS
Участник

ru
Offline Offline

« Ответ #19 : 19-09-2009 11:28 » 

Я на, самом деле, согласен с Вами, что не все устройства обязаны пересылать огромные объемы со страшной скоростью. Но просто у меня устройство, которое служит для записи скоростных потоков данных. Улыбаюсь Поэтому по определению тут же мурашки по коже, когда много Dpc .))) И много программных возобновлений передачи.)))

То есть, тут вообще все гораздо проще. Берем описание буфера из IRP_MJ_READ, указываем направление передачи и вызываем GetScatterGatherList.

Когда адаптер свободен, а в случае одного устройства он свободен сразу, вызывается AdapterControlProcedure. В нее передается сформированный системой Scatter-Gather List, который мы однократно прописываем в устройство поэлементно адрес - длина, адрес - длина.... и просто вызываем передачу и все. Улыбаюсь

Вот тут все бы  ничего в моем случае, но и это уже вроде бы есть в DDK, после окончания передачи система удаляет буфер приема, хотя непонятно тогда, зачем делать PutScatterGatherList. Все, что написал Oney, это для версии DMA 1. В версии DMA 2 появились две новых функции в структуре операций адаптера DMA CalculateScatterGatherList/PutScatterGatherList.

Они позволяют для произвольного буфера памяти вычислить необходимый системе размер Scatter-Gather List для этого буфера и выделить его самому программисту в своем драйвере, а затем просто вызвать BuildScatterGatherList, которая делает то же самое, что и GetScatterGatherList, с тем лишь исключением, что заполняет Scatter - Gather List, адрес которого подставляет в вызове программист. Как мне показалось, это то, что мне нужно. То есть, можно просто проделать все, как пишут по канонам, но ничего не делать в Dpc процедуре, кроме сохранения выделенного самой системой списка в драйвере. А отобразить его, по идее, можно на любой участок виртуальной памяти. Правда, для получения MDL всего списка нужен MDL, из которого этот список был первоначально получен. В случае первичного пользовательского буфера это проблема.

Но, в конце концов, можно потом воспользоваться методом, который рекомендовали Вы в теме о "склейке". Улыбаюсь

Быть может, я вообще зря ношусь с этой идеей, Улыбаюсь и лучше просто выделить один буфер большого размера.
« Последнее редактирование: 19-09-2009 16:45 от Sel » Записан
Sel
Злобный
Администратор

ru
Offline Offline

« Ответ #20 : 19-09-2009 16:49 » 

Offtopic:

SergeiS, это не ICQ, где можно писАть сообщения по 1-2 слова, а форум, где принято высказывать мысли целиком. Пиши связно одним постом. В следующий раз я буду не склеивать твои посты, а просто удалять.
Поставлю в угол.
Записан

Слово не воробей. Всё не воробей, кроме воробья.
SergeiS
Участник

ru
Offline Offline

« Ответ #21 : 21-09-2009 14:37 » 

примеры кода к сожалению сегодня не получились( НА работу так и не попал) Так что завтра....
Записан
PredatorAlpha
Помогающий

us
Offline Offline

« Ответ #22 : 02-10-2009 08:21 » 

Вроде бы большое количество памяти, большее чем ограниченное через пулы можно выделить с помощью MmAllocatePagesForMdl
Но не уверен.
Записан
SergeiS
Участник

ru
Offline Offline

« Ответ #23 : 03-10-2009 22:04 » 

Понял спасибо! Посмотрю описание этой функции более подробно!
Наконец могу привести код который обещался привести. Командировки увы никто не отменял

    pusha
        ; Пробуем построить Scatter/Gather List вручную
        mov eax, [edi].MaxTransfer
        invoke ExAllocatePool, NonPagedPool, eax

        mov [edi].pBuf, eax
        invoke IoAllocateMdl, [edi].pBuf, [edi].MaxTransfer, FALSE, FALSE, NULL
        mov [edi].pMdl, eax
       
        pusha
        invoke DbgPrint, $CTA0("Адрес MDL для буфера памяти - %x"), eax
        popa

        invoke MmBuildMdlForNonPagedPool, [edi].pMdl
       
        pusha
            invoke DbgPrint, $CTA0("MDL создан")
        popa



         mov eax, [edi].pMdl   
         mov eax, (MDL PTR [eax]).ByteCount   
         mov bytes, eax
            pusha
                invoke DbgPrint, $CTA0("Длина полученного MDL для буфера памяти - %x"), eax
            popa

         
        mov eax, [edi].pBuf         
        mov vaddr, eax
       
        ; Вычисляем размер для Scatter/Gather List
        mov edx, [edi].DmaAdapterObject
        mov edx, (DMA_ADAPTER PTR [edx]).DmaOperations

        and eax, NULL
        push eax
        lea eax, SgListSize
        push eax
        push [edi].MaxTransfer
        push [edi].pBuf
        push [edi].pMdl
        push [edi].DmaAdapterObject
        call (DMA_OPERATIONS PTR [edx]).CalculateScatterGatherList

        pusha
            invoke DbgPrint, $CTA0("Статус  - %x размер - %x"), eax, SgListSize
        popa

        mov eax, SgListSize
        mov [edi].SgListSize, eax

        invoke ExAllocatePool, NonPagedPool, [edi].SgListSize
        mov [edi].psgl, eax

        pusha
            invoke DbgPrint, $CTA0("Scatter/Gather List  - %x"), eax
        popa

        push [edi].SgListSize
        push [edi].psgl
        push FALSE
        push edi
        push ProcessSGList
        push bytes
        push vaddr
        push [edi].pMdl
        push DevObj
        push [edi].DmaAdapterObject
        mov edx, [edi].DmaAdapterObject
        mov edx, (DMA_ADAPTER PTR [edx]).DmaOperations
        call (DMA_OPERATIONS PTR [edx]).BuildScatterGatherList

        pusha
            invoke DbgPrint, $CTA0("Scatter/Gather List  Status - %x"), eax
        popa

       
               
       
    popa

и процедура DPC куда передается созданный список блоков

ProcessSGList proc DeviceObject:PDEVICE_OBJECT, pIrp:PIRP, \
    ScatterGather:PSCATTER_GATHER_LIST, \
    Context:PVOID
        pusha
            invoke DbgPrint, $CTA0(" Вызов процедуры ")
        popa
    mov eax, ScatterGather
    assume eax:PTR SCATTER_GATHER_LIST
        pusha
            mov eax, [eax].NumberOfElements

            invoke DbgPrint, $CTA0("Количество элементов %x"), eax
        popa
        pusha
            mov eax, [eax].Elements[0]._Length

            invoke DbgPrint, $CTA0("Длина элемента 0 -  %x"), eax
        popa

   
    assume eax:NOTHING
    ret
ProcessSGList endp


в этом отрывке я просто попытался выделять очень большой буфер из неподкачиваемого пула и потом делать по его MDL описанию Scatter -Gather List. И только потом, поразмыслив я вдруг понял, что обманываю себя ) Из неподкачиваемого пула все равно не выделить большой участок, а построение Scatter/Gather List действительно актуально только если вызов драйвера производится из программы через ReadFile или WriteFile, при этом передается буфер например мегабайт 100 или больше. У меня по крайней мере во время любого запуска список получался из одного блока , разумеется равного по размеру выделенному из неподкачиваемого пула.)
Записан
Serg79
Команда клуба

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

WWW
« Ответ #24 : 04-10-2009 09:27 » 

SergeiS а что, сейчас модно писать реализации функций для драйвера на ассемблере? Или у Тебя там что то такое хитрое реализовывается что С для этого не подойдет?

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

ru
Offline Offline

« Ответ #25 : 04-10-2009 14:55 » 

Serg79 извиняться совершенно не стоит ибо вопрос совершенно правомерный и правильный. Объяснение простое. Во-первых я - давний фанат ассемблера))) Очень люблю ассемблер, а на прошлой моей работе из - за программной цензуры писать нужно было исключительно на C/C++/C#. Эти языки тоже вполне мне нравятся, но тоска по ассемблеру заела последнее время) Вот и скачал masm32 с сайта wasm.ru) И к нему добрые люди сделали пакет KmdKit - ассемблерный вариант основных модулей DDK для Visual C++) Сразу скажу НИЧЕГО в моем драйвере такого нет, что нельзя было бы написать на C) Да и вообще мало что нельзя написать на C и C++. Разве что когда например в сети около нескольких десятков миллионов связей, а нужно так обработать все узлы чтобы время было в пределах пол - секунды)))) Тут уж ассемблерная вставка просто просится) Это - лирические причины)
Есть одна вполне реальная. Вот если задуматься сколько нужно времени и сколько места на диске чтобы установить Visual C++ и DDK ? Пожалуй что на современных платформах довольно быстро да и с нынешними винтами тоже не очень актуальная проблема) Но вот весь пакет для ассемблера ставится где - то минуты за полторы за две чтобы не соврать. Плюс отлаживать драйвер однозначно нужно и в случае C и в случае ассемблера с помощью отладчика ядра. Интегрированная среда разработки и подсветка кода ? Пожалуй что это важно. Но я в принципе нормально набрал текст драйвера и в редакторе masm. Там все проще но достаточно удобно. Причем драйвер у меня не совсем учебный и довольно объемный. Предназначен для работы с высокоскоростным устройством ввода оцифрованного сигнала по нескольким каналам. По большому счету на C это все так же очень хорошо и быстро реализуемо.
Некоторые люди выдвигают еще одну причину, я впрочем с ней далеко не всегда соглашусь. Написав на ассемблере люди считают что гораздо лучше контролируют выходной код. Доля истины в этом есть наверное. Но зная примерно как работает компилятор Visual думаю это не слишком актуально в большинстве случаев) Так что писать на ассемблере можно в двух случаях, точнее в трех: чтобы создать себе ауру особого человека))))), для оптимизации в определенных случаях, и из любви к искусству для души. Поскольку я - человек обычный, то скорее третье и частично второе)))
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines