Медленная связь с использованием общей памяти между пользовательским режимом и ядром - PullRequest
0 голосов
/ 06 марта 2019

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

КОД ЯДРА

typedef struct _SHARED_MEMORY
{
    BOOLEAN mutex;
    CHAR data[BUFFER_SIZE];
} SHARED_MEMORY, *PSHARED_MEMORY;

ZwCreateSection(...)
ZwMapViewOfSection(...)

while (TRUE) {
    if (((PSHARED_MEMORY)SharedSection)->mutex == TRUE) {
      //... do work...
      ((PSHARED_MEMORY)SharedSection)->mutex = FALSE;
    }
    KeDelayExecutionThread(KernelMode, FALSE, &PollingInterval);
}

код приложения

OpenFileMapping(...)
MapViewOfFile(...)

...

RtlCopyMemory(&SM->data, WriteData, Size);
SM->mutex = TRUE;

while (SM->mutex != FALSE) {
    Sleep(1); // Slow and removing it will cause an infinite loop
}

RtlCopyMemory(ReadData, &SM->data, Size);

ОБНОВЛЕНИЕ 1 На данный момент это самое быстрое решение, которое я нашел:

while(InterlockedCompareExchange(&SM->mutex, FALSE, FALSE));

Однако я нахожу забавным, что вам нужно сделать обмен и что нет функции только для сравнения.

1 Ответ

1 голос
/ 06 марта 2019

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

Вам нужно сделать две вещи:

1)Напишите функцию InterlockedGet и используйте ее.

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

Для 1 этоИзвестно, что он работает на всех компиляторах, которые поддерживают InterlockedCompareExchange, по крайней мере в прошлый раз, когда я проверял:

__inline static int InterlockedGet(int *val)
{
    return *((volatile int *)val);
}

Для 2, поместите это как тело цикла ожидания:

__asm
{
    rep nop
}

Для процессоров x86 это определено для решения проблем насыщения ресурсов и прогнозирования ветвлений.

Собираем их вместе:

while ((*(volatile int *) &SM->mutex) != FALSE) {
    __asm
    {
        rep nop
    }
}

Измените int при необходимости, если это не подходит.

...