реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Логирование данных во внешнюю память.
Jenya7
сообщение Feb 4 2015, 09:25
Сообщение #1


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



мне нужно логировать данные во внешнюю память.
каждая запись это структура. думал сделать circular buffer чтоб можно было читать/писать из разных потоков. вопрос есть ли какая нибудь готовая библиотека чтоб не городить свои велосипеды?
Go to the top of the page
 
+Quote Post
toweroff
сообщение Feb 4 2015, 13:32
Сообщение #2


Гуру
******

Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514



Цитата(Jenya7 @ Feb 4 2015, 12:25) *
мне нужно логировать данные во внешнюю память.
каждая запись это структура. думал сделать circular buffer чтоб можно было читать/писать из разных потоков. вопрос есть ли какая нибудь готовая библиотека чтоб не городить свои велосипеды?

что-то типа:
Код
#define    IBUF_LENGTH        (32)
#define    IBUF_MASK        (IBUF_LENGTH-1)

#define    OBUF_LENGTH        (32)
#define    OBUF_MASK        (OBUF_LENGTH-1)

uint32_t ibus_start, ibuf_end, obuf_start, obuf_end;

char    BufOut[OBUF_LENGTH];
char    BufIn[IBUF_LENGTH];

void obuf_init()
{
    obuf_start = obuf_end = 0;
}

void ibuf_init()
{
    ibuf_start = ibuf_end = 0;
}

void put(char c)
{
    while((ibuf_start+IBUF_LENGTH) == ibuf_end);
    BufIn[ibuf_end++ & IBUF_MASK] = c;
}

char get(void)
{
    while(obuf_end == obuf_start);
    return BufOut[obuf_start++ & OBUF_MASK];
}


на скорую руку, так что проверяйте sm.gif

недостаток - размер массивов по степени двойки, тип, разумеется, можно сделать какой нужно
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Feb 4 2015, 14:14
Сообщение #3


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(toweroff @ Feb 4 2015, 19:32) *
что-то типа:
Код
#define    IBUF_LENGTH        (32)
#define    IBUF_MASK        (IBUF_LENGTH-1)

#define    OBUF_LENGTH        (32)
#define    OBUF_MASK        (OBUF_LENGTH-1)

uint32_t ibus_start, ibuf_end, obuf_start, obuf_end;

char    BufOut[OBUF_LENGTH];
char    BufIn[IBUF_LENGTH];

void obuf_init()
{
    obuf_start = obuf_end = 0;
}

void ibuf_init()
{
    ibuf_start = ibuf_end = 0;
}

void put(char c)
{
    while((ibuf_start+IBUF_LENGTH) == ibuf_end);
    BufIn[ibuf_end++ & IBUF_MASK] = c;
}

char get(void)
{
    while(obuf_end == obuf_start);
    return BufOut[obuf_start++ & OBUF_MASK];
}


на скорую руку, так что проверяйте sm.gif

недостаток - размер массивов по степени двойки, тип, разумеется, можно сделать какой нужно


у меня затык как послать структуру. память 32к*8.
Структура
struct record
{
uint32_t timestamp;
uint16_t delta;
}
распаковывать и посылать по байтам?
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 4 2015, 14:20
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(toweroff @ Feb 4 2015, 13:32) *
что-то типа:

на скорую руку, так что проверяйте sm.gif

недостаток - размер массивов по степени двойки, тип, разумеется, можно сделать какой нужно


"...иногда лучше молчать, чем говорить" (с)
Топикстартер явно упомянул многопоточность, а тут конструкции, типа "data[obuf_start++ & OBUF_MASK]".
Треды, атомарность, синхронизация.. слышали про такое ?

Можно сделать закат солнца вручную, т.е. написать свою очередь. Это не сложно. Сложно сделать так, чтобы оно работало правильно.
В самом тупом случае берутся примитивы синхронизации, типа мютексов, пишется тупая кольцевая очередь, где PutData() / GetData() обвешаны этими мютексами как новогодние елки.
Тупо, примитивно, не сильно оптимально по быстродействию и требует наличия ОС.

Если ОС нет или нужно выжать производительность, пишется неблокирующая очередь (а потом еще полгода отлаживается). Тут уже в ход идут такие вещи, как intrinsic функции для атомарного инкремента/декремента, реализация CAS, спинлоки и memory barriers если хотим, шоб оно еще не глючило на многоядерных системах..
.. Это один из любимых вопросов на собеседованиях в конторах типа АРМа..

В промежуточном случае пишутся свои мютексы. Но тут опять же надо очень хорошо разбираться в том, что делаешь.

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




Go to the top of the page
 
+Quote Post
Jenya7
сообщение Feb 4 2015, 15:15
Сообщение #5


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



упрощенно, в первом приближении написал так. как минимум проверю живая ли память.
Код
uint32_t LOGGER_SaveData(uint32_t data)
{
    uint32_t low_byte, high_byte;
    low_byte =  data & 0xFF;
    high_byte =  data >> 8;
    SPIEEPROM_WriteByte(current_address, low_byte);
    SPIEEPROM_WriteByte(current_address+1, high_byte);
    current_address += 2;
    if(current_address>=EEPROM_SIZE)
        current_address = 0;

    return current_address;
}

uint32_t LOGGER_GetData(uint32_t address)
{
    uint32_t low_byte, high_byte;
    low_byte = SPIEEPROM_ReadByte(address);
    high_byte = SPIEEPROM_ReadByte(address+1);
    return (high_byte << 8) | low_byte;
}


Цитата(CrimsonPig @ Feb 4 2015, 20:20) *
"...иногда лучше молчать, чем говорить" (с)
Топикстартер явно упомянул многопоточность, а тут конструкции, типа "data[obuf_start++ & OBUF_MASK]".
Треды, атомарность, синхронизация.. слышали про такое ?

Можно сделать закат солнца вручную, т.е. написать свою очередь. Это не сложно. Сложно сделать так, чтобы оно работало правильно.
В самом тупом случае берутся примитивы синхронизации, типа мютексов, пишется тупая кольцевая очередь, где PutData() / GetData() обвешаны этими мютексами как новогодние елки.
Тупо, примитивно, не сильно оптимально по быстродействию и требует наличия ОС.

Если ОС нет или нужно выжать производительность, пишется неблокирующая очередь (а потом еще полгода отлаживается). Тут уже в ход идут такие вещи, как intrinsic функции для атомарного инкремента/декремента, реализация CAS, спинлоки и memory barriers если хотим, шоб оно еще не глючило на многоядерных системах..
.. Это один из любимых вопросов на собеседованиях в конторах типа АРМа..

В промежуточном случае пишутся свои мютексы. Но тут опять же надо очень хорошо разбираться в том, что делаешь.

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

не горячитесь. вот есть приличный буфер. вопрос как увязать все вместе.
Код
typedef struct struct_cBuffer
{
    unsigned char *dataptr;            ///< the physical memory address where the buffer is stored
    unsigned short size;            ///< the allocated size of the buffer
    unsigned short datalength;        ///< the length of the data currently in the buffer
    unsigned short dataindex;        ///< the index into the buffer where the data starts
} cBuffer;

void bufferInit(cBuffer* buffer, unsigned char *start, unsigned short size)
{
    // begin critical section
    CRITICAL_SECTION_START;
    // set start pointer of the buffer
    buffer->dataptr = start;
    buffer->size = size;
    // initialize index and length
    buffer->dataindex = 0;
    buffer->datalength = 0;
    // end critical section
    CRITICAL_SECTION_END;
}

// access routines
unsigned char  bufferGetFromFront(cBuffer* buffer)
{
    unsigned char data = 0;
    // begin critical section
    CRITICAL_SECTION_START;
    // check to see if there's data in the buffer
    if(buffer->datalength)
    {
        // get the first character from buffer
        data = buffer->dataptr[buffer->dataindex];
        // move index down and decrement length
        buffer->dataindex++;
        if(buffer->dataindex >= buffer->size)
        {
            buffer->dataindex -= buffer->size;
        }
        buffer->datalength--;
    }
    // end critical section
    CRITICAL_SECTION_END;
    // return
    return data;
}

void bufferDumpFromFront(cBuffer* buffer, unsigned short numbytes)
{
    // begin critical section
    CRITICAL_SECTION_START;
    // dump numbytes from the front of the buffer
    // are we dumping less than the entire buffer?
    if(numbytes < buffer->datalength)
    {
        // move index down by numbytes and decrement length by numbytes
        buffer->dataindex += numbytes;
        if(buffer->dataindex >= buffer->size)
        {
            buffer->dataindex -= buffer->size;
        }
        buffer->datalength -= numbytes;
    }
    else
    {
        // flush the whole buffer
        buffer->datalength = 0;
    }
    // end critical section
    CRITICAL_SECTION_END;
}

unsigned char bufferGetAtIndex(cBuffer* buffer, unsigned short index)
{
    // begin critical section
    CRITICAL_SECTION_START;
    // return character at index in buffer
    unsigned char data = buffer->dataptr[(buffer->dataindex+index)%(buffer->size)];
    // end critical section
    CRITICAL_SECTION_END;
    return data;
}

unsigned char bufferAddToEnd(cBuffer* buffer, unsigned char data)
{
    // begin critical section
    CRITICAL_SECTION_START;
    // make sure the buffer has room
    if(buffer->datalength < buffer->size)
    {
        // save data byte at end of buffer
        buffer->dataptr[(buffer->dataindex + buffer->datalength) % buffer->size] = data;
        // increment the length
        buffer->datalength++;
        // end critical section
        CRITICAL_SECTION_END;
        // return success
        return -1;
    }
    // end critical section
    CRITICAL_SECTION_END;
    // return failure
    return 0;
}

unsigned short bufferIsNotFull(cBuffer* buffer)
{
    // begin critical section
    CRITICAL_SECTION_START;
    // check to see if the buffer has room
    // return true if there is room
    unsigned short bytesleft = (buffer->size - buffer->datalength);
    // end critical section
    CRITICAL_SECTION_END;
    return bytesleft;
}

void bufferFlush(cBuffer* buffer)
{
    // begin critical section
    CRITICAL_SECTION_START;
    // flush contents of the buffer
    buffer->datalength = 0;
    // end critical section
    CRITICAL_SECTION_END;
}
Go to the top of the page
 
+Quote Post
Kabdim
сообщение Feb 4 2015, 15:18
Сообщение #6


Знающий
****

Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842



Цитата(CrimsonPig @ Feb 4 2015, 17:20) *
Если ОС нет или нужно выжать производительность, пишется неблокирующая очередь (а потом еще полгода отлаживается).

А зачем самому писать, если можно изучить готовые варианты:
http://www.liblfds.org/ - C, вроде как работает на армах из коробки
http://libcds.sourceforge.net/ - C++, придется запилитить свою прокладку для атомарных операций
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Feb 4 2015, 15:30
Сообщение #7


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(Kabdim @ Feb 4 2015, 21:18) *
А зачем самому писать, если можно изучить готовые варианты:
http://www.liblfds.org/ - C, вроде как работает на армах из коробки
http://libcds.sourceforge.net/ - C++, придется запилитить свою прокладку для атомарных операций


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

Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 4 2015, 15:43
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Jenya7 @ Feb 4 2015, 15:15) *
не горячитесь. вот есть приличный буфер. вопрос как увязать все вместе.


То есть макросы
CRITICAL_SECTION_START
CRITICAL_SECTION_END
уже есть и работают ? В чем вопрос-то тогда ?
Берем лопату и заменяем char на свой тип данных (это если по тупому).

Насчет "приличности" можно сильно поспорить. Это пример того, что я выше назвал "увешан мютексами" как новогодняя елка.
Как указывалось выше, если сделать длину буфера (в единицах данных) равной степени двойки, то адресация по модулю очень сильно упрощается. Применение /, % для таких вещей заставляет меня вздрогнуть.
Далее. Если немного подумать, то можно понять, что увешивать мютексами можно только место, где происходит работа с индексами в буфере. Копирование _данных_ в буфер и из буфера не требует блокировки. Т.е. при помещении данных в буфер сначала копируем данные на незанятое место, только потом обновляем индекс (атомарно). При вынимании данных - в обратном порядке.

> нам бы под ембедед. оптимизированный по скорости.

Гы-гы-гы, с операцией деления и взятия остатка вы далеко уйдете в этом направлении, особенно если данные в буфер будут класться в прерываниях

Сообщение отредактировал CrimsonPig - Feb 4 2015, 15:46
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Feb 4 2015, 15:47
Сообщение #9


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(CrimsonPig @ Feb 4 2015, 21:43) *
То есть макросы
CRITICAL_SECTION_START
CRITICAL_SECTION_END
уже есть и работают ? В чем вопрос-то тогда ?
Берем лопату и заменяем char на свой тип данных (это если по тупому).

Насчет "приличности" можно сильно поспорить. Это пример того, что я выше назвал "увешан мютексами" как новогодняя елка.
Как указывалось выше, если сделать длину буфера (в единицах данных) равной степени двойки, то адресация по модулю очень сильно упрощается. Применение /, % для таких вещей заставляет меня вздрогнуть.
Далее. Если немного подумать, то можно понять, что увешивать мютексами можно только место, где происходит работа с индексами в буфере. Копирование _данных_ в буфер и из буфера не требует блокировки. Т.е. при помещении данных в буфер сначала копируем данные на незанятое место, только потом обновляем индекс (атомарно). При вынимании данных - в обратном порядке.

> нам бы под ембедед. оптимизированный по скорости.

Гы-гы-гы, с операцией деления и взятия остатка вы далеко уйдете в этом направлении, особенно если данные в буфер будут класться в прерываниях

ок. а как передать структуру в буфер. или тупо нарезать ее на байты и посылать побайтово? а при принятии склеивать обратно?
я не спорю буфер можно пооптимизировать. это так сказать скелет.

Сообщение отредактировал Jenya7 - Feb 4 2015, 15:50
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 4 2015, 15:57
Сообщение #10


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Jenya7 @ Feb 4 2015, 15:47) *
ок. а как передать структуру в буфер. или тупо нарезать ее на байты и посылать побайтово? а при принятии склеивать обратно?
я не спорю буфер можно пооптимизировать. это так сказать скелет.


Ну, в самом простейшем случае можно и по байтам туда загонять. Это должно работать (если код рабочий), но будет жутко неэффективно по определению.

Если хочется пихать свои данные то делается так:
- сидим, смотрим в существующий код. Понимаем как оно работает
- выкидываем существующий код
- пишем свой код с блекджеком и шлюхами (void* и memcpy).
- Материмся, отлаживаем, пишем тесты

Ну или можно попытаться найти уже существующие имплементации поторкобезопасной очереди, которая работает с данными более-менее произвольных размеров. Где взять - не знаю sm.gif
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Feb 4 2015, 16:07
Сообщение #11


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(CrimsonPig @ Feb 4 2015, 21:57) *
- сидим, смотрим в существующий код. Понимаем как оно работает

вот я и пытаюсь "смотреть". только пока некуда. sm.gif
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 4 2015, 16:15
Сообщение #12


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502



Цитата(Jenya7 @ Feb 4 2015, 16:07) *
вот я и пытаюсь "смотреть". только пока некуда. sm.gif


А как же реализация буфера из сообщений выше ?

void bufferInit(cBuffer* buffer, unsigned char *start, unsigned short size) и товарищи ?

Выглядит как рабочая (на первый взгляд)... ну, со своими заморочками и недостатками.
Какая разница, что в абстрактный буфер копировать, байт или кусок данных ? Общий принцип-то тот же.
Go to the top of the page
 
+Quote Post
WitFed
сообщение Feb 4 2015, 16:26
Сообщение #13


Местный
***

Группа: Свой
Сообщений: 271
Регистрация: 6-12-11
Из: Taganrog
Пользователь №: 68 701



Я бы порекомендовал от вечных шлюх последнего времени (многопроцессорности) удалиться, тогда реализация мьютекса будет одной командой -- запрещение/разрешение прерываний, которая обычно выносится в интринсики производителем. Вряд ли при запрещённом прерывании сможет случиться нечто, которое переключит задачи и внесёт сумятицу в процесс инкремента указателя, в котором обычно "собака роется".
Упоминание "SPIEEPROM" у ТС говорит о достаточной медлительности самой операции записи, так что разбить исходную структуру на байты или хотя бы short-ы будет нестрашно. Хотя в идеале -- захочется несколько типов данных пихать рано или поздно, тогда в "систему" "прирастёт" длина "пакета", его "тип", время его "случения", где-то рядом с кольцевым буфером надо будет хранить его текущие позиции "начала" и "конца"... Короче, получится настоящая трассировка всего, чего "приспичит" отладить "вживую", а потом "выгрузиться" в удобной форме... Эта тема очень объёмная в идеале, даже если не трогать "настоящих шлюх", а для них легче тогда устроить отдельные ЦБ для каждого ядра, потом сливать...
Лучше бы "EEPROM" делать в конце всего исполнения -- когда "усё пропало", а до того складироваться в ОЗУ -- оно обычно быстрее. Ну и если опереции "EEPROM" нереентерабельные -- их тоже придётся под мьютекс затягивать.
Go to the top of the page
 
+Quote Post
Jenya7
сообщение Feb 4 2015, 16:26
Сообщение #14


Профессионал
*****

Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075



Цитата(CrimsonPig @ Feb 4 2015, 22:15) *
А как же реализация буфера из сообщений выше ?

void bufferInit(cBuffer* buffer, unsigned char *start, unsigned short size) и товарищи ?

Выглядит как рабочая (на первый взгляд)... ну, со своими заморочками и недостатками.
Какая разница, что в абстрактный буфер копировать, байт или кусок данных ? Общий принцип-то тот же.

да вот думаю как увязать все вместе.
Go to the top of the page
 
+Quote Post
CrimsonPig
сообщение Feb 4 2015, 16:37
Сообщение #15


Местный
***

Группа: Участник
Сообщений: 329
Регистрация: 23-04-14
Пользователь №: 81 502




Цитата(Jenya7 @ Feb 4 2015, 16:26) *
да вот думаю как увязать все вместе.


А, кстати, есть еще один тонкий момент... что такое structure members padding знаете ?
При записи своих структур в eeprom может доставить немало веселых моментов sm.gif

Сообщение отредактировал CrimsonPig - Feb 4 2015, 16:38
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th July 2025 - 23:03
Рейтинг@Mail.ru


Страница сгенерированна за 0.01489 секунд с 7
ELECTRONIX ©2004-2016