Разделяемая память и ее защита.
От: Ivych  
Дата: 04.12.18 19:31
Оценка:
Добрый вечер, форумчане!

Имеется вопрос, есть в проекте (операционка QNX4.25) несколько разделяемых объектов памяти, для взаимодействия между процессами. Есть один писатель в каждую из них и несколько читателей, я написал несколько функций оберток вида:

void XXXX_ShmemLock()
{
    assert( g_Shmem );
    sem_wait( &g_Shmem->lock );
}

void XXXX_ShmemUnlock()
{
    assert( g_Shmem );
    sem_post( &g_Shmem->lock );
}

void XXXX_ShmemCopy( void *destination, const void *source, const size_t num )
{
    XXXX_ShmemLock();
    _disable();
    memcpy( destination, source, num );
    _enable();
    XXXX_ShmemUnlock();
}


Дальнейшие операции более высокого уровня, выполняются путем вызова функций-оберток, как пример приложу:
void XXXX_ReadADC( const ADCChannel_t channel, double *voltage )
{
    assert( channel < ADC_ChannelsNum );
//    *voltage = g_Shmem->Vin[channel];
    XXXX_ShmemCopy( (void *)voltage, (const void *)&g_Shmem->Vin[channel], sizeof(g_Shmem->Vin[channel]) );
}

void XXXX_WriteADC( const ADCChannel_t channel, const double voltage )
{
    assert( channel < ADC_ChannelsNum );
//    g_Shmem->Vin[channel] = voltage;
    XXXX_ShmemCopy( (void *)&g_Shmem->Vin[channel], (const void *)&voltage, sizeof(voltage) );
}

P.S. Насколько рационален такой подход?

Так вот, как видно в функции XXXX_ShmemCopy, копирование данных в область и из нее, производится вызовом memcpy, обернутым в семафор и дополнительным отключением прерываний на момент копирования, чтобы обеспечить атомарность операции. Есть ли вообще смысл в такой перестраховке на однопроцессорной системе? Минус в использовании _disable()/_enable() при больших объемах копирования (которых скорее всего не будет), на момент выполнения операции, стопорится работа всей системы, или если вызовов XXXX_ShmemCopy будет много (а их скорее всего будет много), то рискуем то и делать что бесконечно выключать/включать прерывания. Так же есть ли необходимость оборачивания простых операций вида "Область->переменная = что-то записать", понятное дело не делая справа от "=" сложных операций, будет ли такая операция выполнена атомарно?
qnx unix posix semaphore shared
Re: Разделяемая память и ее защита.
От: reversecode google
Дата: 04.12.18 21:51
Оценка:
wait/post является атомарным объектом для синхронизации к шаред мемори
значит sti/cli не нужно
да операционка может прервать ваше копирование
но какое это отношение имеет в разделяемой памяти ?
Re: Разделяемая память и ее защита.
От: lpd Черногория  
Дата: 05.12.18 09:47
Оценка:
Здравствуйте, Ivych, Вы писали:


I>Так вот, как видно в функции XXXX_ShmemCopy, копирование данных в область и из нее, производится вызовом memcpy, обернутым в семафор и дополнительным отключением прерываний на момент копирования, чтобы обеспечить атомарность операции. Есть ли вообще смысл в такой перестраховке на однопроцессорной системе?

На однопроцессорной машине достаточно одного запрета прерываний.

I> Минус в использовании _disable()/_enable() при больших объемах копирования (которых скорее всего не будет), на момент выполнения операции, стопорится работа всей системы, или если вызовов XXXX_ShmemCopy будет много (а их скорее всего будет много), то рискуем то и делать что бесконечно выключать/включать прерывания.

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

I> Так же есть ли необходимость оборачивания простых операций вида "Область->переменная = что-то записать", понятное дело не делая справа от "=" сложных операций, будет ли такая операция выполнена атомарно?


Нужно использовать atomic_* функции или atomic_t тип. Выражение справа роли не играет.
У сложных вещей обычно есть и хорошие, и плохие аспекты.
Берегите Родину, мать вашу. (ДДТ)
Отредактировано 06.12.2018 7:19 lpd . Предыдущая версия . Еще …
Отредактировано 05.12.2018 10:39 lpd . Предыдущая версия .
Re: Разделяемая память и ее защита.
От: -prus-  
Дата: 05.12.18 14:54
Оценка:
I>Так же есть ли необходимость оборачивания простых операций вида "Область->переменная = что-то записать", понятное дело не делая справа от "=" сложных операций, будет ли такая операция выполнена атомарно?

А что, если shared-memory buffer представить в виде кольцевого буфера, разбитого на блоки вида, например:
struct block
{
    int flags;
    char buffer[...]
};


Сначала у всех блоков flags = ready to write. Когда писателю нужно записать что-то, он ищет первый свободный для себя блок (flags = ready to write), копирует в буфер данные и выставляет flags = ready to read. Читатели в свою очередь пробегаются по кольцевому буферу, ждут flags = ready to read и, когда данные есть, читают их и по окончанию выставляют блоку снова flags = ready to write. Не?
С уважением,
Евгений
Re[2]: Разделяемая память и ее защита.
От: a7d3  
Дата: 05.12.18 15:34
Оценка:
Здравствуйте, -prus-, Вы писали:

P>Сначала у всех блоков flags = ready to write. Когда писателю нужно записать что-то, он ищет первый свободный для себя блок (flags = ready to write), копирует в буфер данные и выставляет flags = ready to read. Читатели в свою очередь пробегаются по кольцевому буферу, ждут flags = ready to read и, когда данные есть, читают их и по окончанию выставляют блоку снова flags = ready to write. Не?


Это очень медленно, потому что префетчер в ЦПУ не справится с предсказанием какие именно данные из ОЗУ подгружать в кэш ЦПУ.
Лучше, когда есть отдельный массив для флагов, целиком помещающийся в одну или несколько кэш-линий.
Тогда:
  1. Проверка флагов для поиска свободного блока будет происходить максимально быстро
  2. Данные записываемые в ОЗУ на самом деле будут оседать в кэше ЦПУ выталкиваясь в буффер записи ОЗУ.
  3. Ошибки префетчера будут аффектить производительность лишь чтение данных из ОЗУ, если она производилось очень давно или же делал её ЦПУ на другом сокете.
Re[2]: Разделяемая память и ее защита.
От: Ivych  
Дата: 06.12.18 18:18
Оценка:
Здравствуйте, lpd, Вы писали:

lpd>>На однопроцессорной машине достаточно одного запрета прерываний.

Да, но судя по всему плохая идея использовать их в ОСРВ.

lpd>>Семафоров же одних достаточно.

Именно в таком виде сейчас и оставил.

lpd>>Нужно использовать atomic_* функции или atomic_t тип. Выражение справа роли не играет.

Таких в QNX 4.25 не имеется.

Спасибо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.