Как я делаю динамическую передачу данных и управление памятью между потоками в C? - PullRequest
1 голос
/ 27 марта 2009

Платформа: ARM9

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

Требования - обычный C, без внешних библиотек и без наддува.

ОС - REX RTOS

У меня есть два потока на встроенной платформе -

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

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

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

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

Ответы [ 4 ]

3 голосов
/ 27 марта 2009

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

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

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

удачи!

0 голосов
/ 30 марта 2009

Моей первой мыслью было бы использовать круговые буферы. Вот пример кода. Не стесняйтесь адаптировать это к своим собственным потребностям. Вы, вероятно, не хотели бы глобальные переменные. И вы можете не захотеть #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 () для копирования в циркулярный буфер.

Главной особенностью этого кода является то, как он обрабатывает ptr для add и rmv.


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

вашей платформы.

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

0 голосов
/ 30 марта 2009

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

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

0 голосов
/ 27 марта 2009

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...