Как мне выполнить динамическую передачу данных и управление памятью между потоками в C?

StackOverflow https://stackoverflow.com/questions/689514

  •  22-08-2019
  •  | 
  •  

Вопрос

Платформа:ARM9

Язык программирования C

Требования — простой C, никаких внешних библиотек и ускорения.

ОС — REX RTOS

У меня есть два потока, работающих на встроенной платформе:

  1. один находится на уровне драйвера, обрабатывая все коммуникации и передачу данных с помощью оборудования.
  2. второй поток запускает приложение, которое использует данные в/из оборудования.

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

Моя проблема заключается в том, что данные, полученные от оборудования, могут быть динамическими, т.е.мы заранее не знаем, сколько памяти поток приложения должен выделить для каждого запроса к оборудованию или от него, поскольку это определяется во время выполнения.

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

Это было полезно?

Решение

На ум приходит пара вариантов:

1) выделите память в драйвере, освободите ее в приложении.Но...мы склонны избегать использования malloc во всем, что приближается к требованиям реального времени.Если у вас есть доступ к malloc/free и нет проблем «в реальном времени» или проблем с фрагментацией памяти (т. е.ваша куча достаточно велика), то это довольно простой подход.Драйвер просто отправляет выделенный указатель в поток приложения через очередь сообщений, и после завершения приложение освобождает память.Следите за утечками памяти.

2) Кольцевые или круговые буферы.Драйвер полностью управляет кольцевым буфером фиксированного размера и просто отправляет сообщение приложению, когда буфер готов.Подробности смотрите здесь: Круговой буфер.Затем приложение снова помечает данные как «доступные» через API драйвера, что помогает скрыть детали кольцевого буфера от потока приложения.Мы используем этот подход для одного из наших драйверов, у которого очень похожий набор требований, как вы описываете.В этом случае вам нужно позаботиться об определении «лучшего» размера кольцевого буфера, обработке переполнения в драйвере и т. д.

удачи!

Другие советы

Вы не указываете ОС, но у вас каким-то образом есть "потоки".За исключением того, что один из них находится на уровне драйвера (обработчик прерываний), а другой похож на приложение (пользовательское пространство/ядро).Но это тоже не совпадает, потому что ваш драйвер и приложение взаимодействуют еще до того, как данные будут обработаны.

Ваша терминология сбивает с толку и не обнадеживает.Это доморощенная (RT) ОС или нет?

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

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

Поскольку это C, мне пришлось заставить приложение зарегистрировать обратный вызов с драйвером.Цель обратного вызова — обработка данных после того, как драйвер прочитает их с устройства.Драйвер управляет памятью, т.е.выделяет память, вызывает обратный вызов и, наконец, освобождает память.Кроме того, обратный вызов имеет разрешение только на чтение памяти.Поэтому в идеале приложение должно просто скопировать содержимое буфера в свою память и сразу же выйти из обратного вызова.Затем он может свободно обрабатывать данные, когда и как пожелает.

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

Моей первой мыслью было бы использовать круговые буферы.Вот пример кода.Не стесняйтесь адаптировать это для своих целей.Вероятно, вам не нужны глобальные переменные.И вам может не понадобиться #defines:

#define LENGTH (1024)
#define MASK (LENGTH-1)
uint8 circularBuffer[ LENGTH ];
int circularBuffer_add = 0;
int circularBuffer_rmv = 0;

void copyIn( uint8 * circularBuffer, uint8 * inputBuffer, int n ) {
    int i;
    for( i = 0; i < n; i++ ) {
        circularBuffer[ circularBuffer_add ] = inputBuffer[ i ];
        circularBuffer_add = ( circularBuffer_add + 1 ) & MASK;
    } 
}

void copyOut( uint8 * circularBuffer, uint8 * outputBuffer, int n ) {
    int i;
    for( i = 0; i < n; i++ ) {
        outputBuffer[ i ] = circularBuffer[ circularBuffer_rmv ];
        circularBuffer_rmv = ( circularBuffer_rmv + 1 ) & MASK;
    } 
}

Также в приведенном выше коде предполагается, что ваша единица данных имеет тип данных «uint8».Вы можете изменить его, чтобы он использовал другой тип данных.Или вы можете даже сделать его универсальным и использовать memcpy() для копирования в круговой буфер.

Основная особенность этого кода — то, как он обрабатывает add и rmv ptr.


Как только вы начнете работать с приведенным выше кодом. Я предлагаю в какой-то момент переключить все операции чтения с оборудования на использование вашей платформы. прямой доступ к памяти API.

Это важно переключиться на прямой доступ к памяти, поскольку приведенный выше код использует много циклов по сравнению с DMA. который использует почти нулевые циклы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top