использовать va_list из другого потока

Я работаю с FreeRTOS (newlib) в небольшой встроенной системе и обнаружил, что printf и семейство тратят огромное количество места в стеке. У меня много задач, и мне не хватает оперативной памяти, чтобы сделать стек для каждой достаточно большим, чтобы поддерживать вызовы printf(). Чтобы обойти это, я работаю над созданием задачи «сервер печати», которая будет иметь большой стек и выполнять все функции printf() от имени других задач.

Итак, мой вопрос: как правильно передать va_list в другой поток (задачу)? В приведенном ниже примере создаются параметры мусора.

Коротко о том, как это работает: task_printf() поместит свои параметры в статические переменные, а затем даст сигнал server_task выполнить реальную печать. Когда server_task выполнена, это сигнализирует клиенту о продолжении.

// printf parameters
static va_list static_args;
static const char *static_format;
static int static_result;


// printf server task.  Processes printf requests forever
void server_task(void *pvParameters)
{
    while(1)
    {
        xSemaphoreTake(printf_start, portMAX_DELAY);  // wait for start command
        static_result = vprintf(static_format, static_args);
        xSemaphoreGive(printf_finished);  // give finish signal
    }
}


// get server task to print something for us
int task_printf(const char *format, ...)
{
    int result;

    xSemaphoreTake(printf_mutex, portMAX_DELAY); // lock

    va_start(static_args, format);
    static_format = format;

    xSemaphoreGive(printf_start);  // give start signal
    xSemaphoreTake(printf_finished, portMAX_DELAY);  // wait for completion

    va_end(static_args);

    ...
}

person goertzenator    schedule 20.07.2012    source источник
comment
Возможно, имеет смысл исправить/заменить реализацию printf в newlib, чтобы не требовалось столько места в стеке? Я знаю, что моя реализация printf в musl libc использует менее 1 КБ для строк формата, которые не включают плавающую точку.   -  person R.. GitHub STOP HELPING ICE    schedule 21.07.2012
comment
Размер стека по умолчанию для FreeRTOS составляет 320 байт, и этого достаточно для большинства задач, поэтому требуется дополнительный 1 КБ ОЗУ для каждой задачи, когда у меня их много, что приводит к большому количеству потраченной впустую ОЗУ. У меня есть около 30 КБ для работы на микроконтроллере, который я использую.   -  person goertzenator    schedule 22.07.2012
comment
Вау, это довольно мало. В моей реализации прямо сейчас используется около 850 байт, но я думаю, что его можно было бы сократить до 400, если бы были удалены некоторые оптимизации скорости и переупорядочение аргументов XSI i18n. С учетом сказанного, я думаю, у вас может быть правильный подход.   -  person R.. GitHub STOP HELPING ICE    schedule 22.07.2012


Ответы (1)


Ну, вышесказанное действительно работает сейчас. Я напортачил с инициализацией семафора (не показано), что позволило стеку вызывающей стороны удалить аргументы до того, как сервер printf смог их использовать.

person goertzenator    schedule 20.07.2012
comment
Я ищу что-то подобное, но оно не должно блокировать вызывающую задачу (как это делает ваше решение). Любая идея, как создать своего рода копию va_list и отправить ее другой задаче через очередь? - person LPG; 30.07.2014
comment
Если у вас есть С++ 11, вы, вероятно, могли бы реализовать task_printf как вариативный шаблон, который формирует закрытие, а затем ставит его в очередь на сервер, не дожидаясь ответа. Но, даже если это работает на небольшом UC, держу пари, это хуже, чем прямой вызов printf. Я сделал кое-что на С++ 11 на UC, но не на лямбдах и замыканиях. - person goertzenator; 22.08.2014