Поделиться через


Ошибки в многопроцессорной среде

В операционной системе на основе NT драйверы многопоточные; они могут одновременно получать несколько запросов ввода-вывода из разных потоков. При разработке драйвера необходимо предположить, что он будет работать в системе SMP, и принять соответствующие меры для обеспечения целостности данных.

В частности, всякий раз, когда водитель изменяет глобальные данные или данные файлового объекта, он должен использовать блокировку или взаимоблокированную последовательность для предотвращения условий гонки.

Обнаружение состояния гонки при ссылке на глобальные или файловые данные объекта

В следующем фрагменте кода состояние гонки может возникнуть, когда водитель обращается к глобальным данным в Data.LpcInfo:

   PLPC_INFO pLpcInfo = &Data.LpcInfo; //Pointer to global data
   ...
   ...
   // This saved pointer may be overwritten by another thread.
   pLpcInfo->LpcPortName.Buffer = ExAllocatePool(
                                     PagedPool,
                                     arg->PortName.Length);

Несколько потоков, внося этот код в результате вызова IOCTL, могут привести к утечке памяти при перезаписи указателя. Чтобы избежать этой проблемы, драйвер должен использовать подпрограммы ExInterlockedXxx или какой-либо тип блокировки при изменении глобальных данных. Требования драйвера определяют допустимые типы блокировок. Дополнительные сведения см. в разделах Spin Locks, Kernel Dispatcher Objects и ExAcquireResourceSharedLite.

В следующем примере выполняется попытка перераспределить буфер для конкретного файла (Endpoint-LocalAddress>) для хранения адреса конечной точки:

   Endpoint = FileObject->FsContext;

    if ( Endpoint->LocalAddress != NULL &&
         Endpoint->LocalAddressLength <
                   ListenEndpoint->LocalAddressLength ) {

      FREE_POOL (Endpoint->LocalAddress,
                 LOCAL_ADDRESS_POOL_TAG
                 );
      Endpoint->LocalAddress  = NULL;
   }

    if ( Endpoint->LocalAddress == NULL ) {
       Endpoint->LocalAddress =
            ALLOCATE_POOL (NonPagedPool,
                           ListenEndpoint->LocalAddressLength,
                           LOCAL_ADDRESS_POOL_TAG);
   }

В этом примере может возникнуть состояние гонки с доступом к объекту файла. Так как драйвер не содержит блокировок, в эту функцию могут входить два запроса для одного и того же объекта файла. Результатом могут быть ссылки на освобожденную память, несколько попыток освобождения одной и той же памяти или утечки памяти. Чтобы избежать этих ошибок, два оператора if должны быть заключены в спин-блокировку.