14 votos

¿Cuáles son los beneficios de un non-preemptive OS? y el precio de estos beneficios?

Para un desnudo de metal MCU, en Comparación con el casero código de fondo con el bucle plus temporizador de interrupción de la arquitectura, ¿cuáles son los beneficios de un non-preemptive OS? Lo que entre estos beneficios son suficientemente atractivos para que un proyecto para adoptar un non-preemptive OS, en lugar de para uso casero código de fondo con el lazo de la arquitectura?
.

Explicación a la Pregunta:

Realmente agradezco a todos aquellos que han respondido a mi pregunta. Siento que la respuesta ha sido casi allí. Añado esta explicación a mi pregunta aquí donde se muestra mi propia consideración y puede ayudar a limitar la cuestión o hacer que sea más precisa.

Lo que estoy tratando de hacer es entender cómo elegir el más adecuado RTOS para un proyecto en general.
Para lograr esto, una mejor comprensión de los conceptos básicos y el más atractivo de los beneficios de los diferentes tipos de RTO y el precio correspondiente será de ayuda, ya que no hay mejor RTOS para todas las aplicaciones.
He leído libros sobre OS hace un par de años pero no los tengo conmigo. He buscado en internet antes he publicado mi pregunta aquí y encontré esta información ha sido muy útil: http://www.ustudy.in/node/5456.
Hay un montón de otra información útil, como el de las presentaciones en la página web de diferentes RTOS, artículos comparando preferente en la programación y no preferente, programación, etc.
Pero no encontré ningún tema mencionado al elegir un non-preemptive RTOS y cuando es mejor escribir su propio código utilizando el temporizador de interrupción y de fondo en bucle.
Tengo ciertas mis propias respuestas, pero no estoy bastante satisfecho con ellos.
Realmente me gustaría saber la respuesta o la vista de más expeirenced personas, especialmente en las prácticas de la industria.

A mi entender hasta ahora es:
no importa el uso o no uso de un sistema operativo, cierto tipo de programación de los códigos son siempre necesarias, incluso en la forma de código como:

    in the timer interrupt which occurs every 10ms  
    if(it's 10ms)  
    {  
      call function A / execute task A;  
    }  
    if(it's 50ms)  
    {  
      call function B / execute task B;  
    }  

Beneficio 1:
Un non-preemptive OS designa el modo o estilo de programación para la programación de código, de modo que los ingenieros pueden compartir el mismo punto de vista, aunque no lo fueran en el mismo proyecto antes. A continuación, con el mismo punto de vista acerca del concepto de tarea, los ingenieros pueden trabajar en diferentes tareas y ponerlas a prueba, el perfil de ellos de forma independiente tanto como sea posible.
Pero, ¿cuánto somos realmente capaces de obtener de esto? Si los ingenieros están trabajando en el mismo proyecto, se puede encontrar la manera de compartir el mismo punto de vista bien sin el uso de un non-preemptive OS.
Si un ingeniero es de otro proyecto o de la empresa, él se beneficiará si él sabía que el OS antes. Pero si él no lo hizo, de nuevo, parece que no hacen gran diferencia para él para aprender un nuevo sistema operativo o una nueva pieza de código.

Beneficio 2:
Si el sistema operativo de código ha sido bien probado, con lo que se ahorra el tiempo de depuración. Este es realmente un buen beneficio.
Pero si la aplicación sólo tiene unos 5 tareas, creo que no es muy complicado escribir su propio código utilizando el temporizador de interrupción y de fondo en bucle.

Un non-preemptive OS aquí se refiere a un comercial / gratis / OS con un programador no preferente.
Cuando he publicado esta pregunta, yo principalmente pensar en algunos sistemas operativos, como:
(1) un BESO Kernel (Una Pequeña NonPreemptive RTOS - reclamada por su página web)
http://www.frontiernet.net/~rhode/kisskern.html
(2) uSmartX (ligero, RTO - reclamada por su página web)
(3) FreeRTOS (Es un preventivo, RTO, pero como yo lo entiendo, puede ser configurado como un non-preemptive RTOS)
(4) de uC/OS (similares como FreeRTOS)
(5) el legado de OS / programador de código en algunas empresas (generalmente hecha y mantenida por la empresa internamente)
(No se puede agregar más enlaces debido a la limitación de nuevo StackOverflow cuenta)

Como yo lo entiendo, un non-preemptive OS es una colección de estos códigos:
(1) un programador con non-preemptive de estrategia.
(2) instalaciones para la tarea de la comunicación, exclusión mutua, la sincronización y el control del tiempo.
(3) la gestión de la memoria.
(4) otros útiles instalaciones / bibliotecas como Sistema de Archivos, pila de red, interfaz gráfica de usuario y etc. (FreeRTOS y admisión a la uC/OS ofrece estos, pero no estoy seguro de si todavía funciona cuando el programador está configurado como no preferente)
Algunos de ellos no están siempre allí. Pero el programador es una necesidad.

13voto

Mark Puntos 1998

Esto huele un poco off-topic pero voy a tratar de guiarlo de vuelta en la pista.

Preventiva multitarea significa que el sistema operativo o kernel puede suspender el subproceso actualmente en ejecución y cambiar a otro según sea la programación heurística que tiene en el lugar. La mayoría de las veces los subprocesos que se ejecutan no tienen el concepto de que hay otras cosas que suceden en el sistema, y lo que esto significa para su código es que se debe ser cuidadoso diseño de modo que si el núcleo decide suspender un hilo en el centro de un multi-paso de la operación (es decir el cambio de una salida PWM, la selección de un nuevo canal ADC, el estado de lectura de un I2C periféricos, etc.) y otro hilo de ejecución por un tiempo, que estos dos hilos no interfieren el uno con el otro.

Un arbitrario ejemplo: digamos que usted es nuevo a multiproceso sistemas embebidos y tiene un pequeño sistema con un I2C ADC, un LCD SPI y I2C EEPROM. Se decidió que sería una buena idea tener dos hilos: uno en el que se lee desde el ADC y escribe las muestras a la EEPROM, y uno que lee el último 10 muestras, los promedios de ellos y los muestra en el LCD SPI. La experiencia de diseño sería algo como esto (extremadamente simplificada):

char i2c_read(int i2c_address, char databyte)
{
    turn_on_i2c_peripheral();
    wait_for_clock_to_stabilize();

    i2c_generate_start();
    i2c_set_data(i2c_address | I2C_READ);
    i2c_go();
    wait_for_ack();
    i2c_set_data(databyte);
    i2c_go();
    wait_for_ack();
    i2c_generate_start();
    i2c_get_byte();
    i2c_generate_nak();
    i2c_stop();
    turn_off_i2c_peripheral();
}

char i2c_write(int i2c_address, char databyte)
{
    turn_on_i2c_peripheral();
    wait_for_clock_to_stabilize();

    i2c_generate_start();
    i2c_set_data(i2c_address | I2C_WRITE);
    i2c_go();
    wait_for_ack();
    i2c_set_data(databyte);
    i2c_go();
    wait_for_ack();
    i2c_generate_start();
    i2c_get_byte();
    i2c_generate_nak();
    i2c_stop();
    turn_off_i2c_peripheral();
}

adc_thread()
{
    int value, sample_number;

    sample_number = 0;

    while (1) {
        value = i2c_read(ADC_ADDR);
        i2c_write(EE_ADDR, EE_ADDR_REG, sample_number);
        i2c_write(EE_ADDR, EE_DATA_REG, value);

        if (sample_number < 10) {
            ++sample_number;
        } else {
            sample_number = 0;
        }
    };
}

lcd_thread()
{
    int i, avg, sample, hundreds, tens, ones;

    while (1) {
        avg = 0;
        for (i=0; i<10; i++) {
            i2c_write(EE_ADDR, EE_ADDR_REG, i);
            sample = i2c_read(EE_ADDR, EE_DATA_REG);
            avg += sample;
        }

        /* calculate average */
        avg /= 10;

        /* convert to numeric digits for display */
        hundreds = avg / 100;
        tens = (avg % 100) / 10;
        ones = (avg % 10);

        spi_write(CS_LCD, LCD_CLEAR);
        spi_write(CS_LCD, '0' + hundreds);
        spi_write(CS_LCD, '0' + tens);
        spi_write(CS_LCD, '0' + ones);
    }
}

Este es un muy crudo y rápido ejemplo. No de código como este!

Ahora, recuerde, preferente multitarea OS puede suspender cualquiera de estos hilos en cualquier línea en el código (en realidad en cualquier asamblea de instrucción) y dar el hilo de otro tiempo.

Pensar acerca de eso. Imagina qué pasaría si el sistema operativo ha decidido suspender adc_thread() entre el valor de los EE dirección de escribir y la escritura de los datos reales. lcd_thread() , estiércol alrededor con el I2C periférica para leer los datos que necesitaba, y cuando adc_thread() le tocó el turno a correr de nuevo, la memoria EEPROM no estar en el mismo estado en el que estaba a la izquierda. Las cosas no funcionan muy bien en todo. Peor aún, podría incluso trabajar la mayoría del tiempo, pero no todo el tiempo, y que iba a volver loco tratando de averiguar por qué su código no está funcionando cuando se VE como debería!

Ese es el mejor ejemplo de caso; el sistema operativo podría decidir adelantarse i2c_write() de adc_thread()'s de contexto y empezar a correr de nuevo desde lcd_thread()'s de contexto! Las cosas se pueden poner muy complicado realmente rápido.

Cuando estás escribiendo código para que funcione en un preferente en entorno multitarea tienes que utilizar mecanismos de bloqueo para asegurarse de que si el código está suspendida en un momento inoportuno que todo el infierno no soltarse.

Multitarea cooperativa, en el otro lado, significa que cada hilo está en el control de cuando se da su tiempo de ejecución. La codificación es más simple, pero el código debe ser diseñado cuidadosamente para asegurarse de que todos los hilos con el tiempo suficiente para que se ejecute. Otro ejemplo inventado:

char getch()
{
    while (! (*uart_status & DATA_AVAILABLE)) {
        /* do nothing */
    }

    return *uart_data_reg;
}

void putch(char data)
{
    while (! (*uart_status & SHIFT_REG_EMPTY)) {
        /* do nothing */
    }

    *uart_data_reg = data;
}

void echo_thread()
{
    char data;

    while (1) {
        data = getch();
        putch(data);
        yield_cpu();
    }
}

void seconds_counter()
{
    int count = 0;

    while (1) {
        ++count;
        sleep_ms(1000);
        yield_cpu();
    }
}

Que el código no cómo el trabajo que usted piensa, o incluso si no parece funcionar, no funciona como la tasa de datos de la eco hilo aumenta. De nuevo, vamos a tomar un minuto para mirar.

echo_thread() espera un byte a aparecer en una UART y, a continuación, se pone y espera hasta que exista espacio para escribir, escribe. Después de que se hace se da a los otros hilos de una vez para que se ejecute. seconds_counter() , se incrementará un recuento, espere a 1000ms y, a continuación, dar a los otros subprocesos una oportunidad para ejecutar. Si dos bytes vienen a la UART, mientras que a largo sleep() que está sucediendo, usted podría perder la oportunidad de verlos porque nuestro hipotético UART no tiene FIFO para almacenar los caracteres, mientras que la CPU está ocupada haciendo otras cosas.

La manera correcta para implementar esta muy mal ejemplo sería el poner yield_cpu() donde cada vez que usted tiene un bucle ocupado. Esto ayudará a que las cosas se mueven a lo largo, pero puede causar otros problemas. por ejemplo, si el tiempo es crítico, y que el rendimiento de la CPU a otro hilo que tarda más de lo esperado, podría tener su tiempo arrojado. Un derecho preferente multitarea OS no tienen este problema porque la fuerza suspende hilos para asegurarse de que todos los hilos están programados correctamente.

Ahora, ¿qué es esto que tengo que hacer con un temporizador y de fondo en bucle? El temporizador y el fondo de bucle son muy similares a los de multitarea cooperativa ejemplo anterior:

void timer_isr(void)
{
    ++ticks;
    if ((ticks % 10)) == 0) {
        ten_ms_flag = TRUE;
    }

    if ((ticks % 100) == 0) {
        onehundred_ms_flag = TRUE;
    }

    if ((ticks % 1000) == 0) {
        one_second_flag = TRUE;
    }
}

void main(void)
{
    /* initialization of timer ISR, etc. */

    while (1) {
        if (ten_ms_flag) {
            if (kbhit()) {
                putch(getch());
            }
            ten_ms_flag = FALSE;
        }

        if (onehundred_ms_flag) {
                    get_adc_data();
            onehundred_ms_flag = FALSE;
        }

        if (one_second_flag) {
            ++count;
                    update_lcd();
            one_second_flag = FALSE;
        }
    };
}

Esto se ve muy cerca de la cooperativa de roscado ejemplo; usted tiene un temporizador que establece los eventos y un bucle principal que busca y actúa sobre ellos en una atómica de la moda. Usted no tiene que preocuparse acerca de la ADC y la pantalla LCD "hilos" interferir el uno con el otro, porque uno nunca interrumpa a la otra. Usted todavía tiene que preocuparse acerca de un "hilo" tomando demasiado tiempo; por ejemplo, qué sucede si get_adc_data() toma 30ms? te echaremos de menos en tres oportunidades a la verificación de un carácter y de eco.

El bucle+temporizador de aplicación es a menudo mucho más sencillo de implementar que una cooperativa multitasked micronúcleo ya que su código puede ser diseñado más específico para la tarea en mano. No son realmente multitarea tanto como el diseño de un sistema fijo donde dar a cada subsistema de algún tiempo para hacer sus tareas de una forma muy específica y predecible. Incluso una cooperativa multitasked sistema tiene que tener un genérico estructura de tarea para cada subproceso y el siguiente hilo de ejecución está determinado por una función de programación que puede llegar a ser bastante complejo.

Los mecanismos de bloqueo para los tres sistemas son el mismo, pero la sobrecarga necesaria para cada uno es muy diferente.

Personalmente, yo casi siempre que el código de esta última norma, el bucle+temporizador de aplicación. Me parece threading, es algo que debe ser utilizado con moderación. No sólo es más complejo para escribir y depurar, pero también requiere más de la cabeza (una multitarea preferente micronúcleo siempre va a ser más grande que un estúpidamente simple temporizador y bucle principal evento seguidor).

También hay un refrán que dice que cualquier persona que trabaje en la rosca se llega a apreciar:

if you have a problem and use threads to solve it, yoeu ndup man with y pemro.bls

:-)

6voto

RelaXNow Puntos 1164

La multitarea puede ser una abstracción útil en una gran cantidad de microcontroladores proyectos, aunque un verdadero preventiva programador sería demasiado pesado e innecesario en la mayoría de los casos. He hecho más de 100 microcontrolador proyectos. He utilizado cooperativa de encargar a un número de veces, pero preventiva de cambiar de tarea con sus asociados de equipaje hasta ahora no ha sido adecuada.

Los problemas con preferente en la asignación de tareas como aposición a la cooperativa de tareas son:

Mucho más pesado. Preventivo a los programadores de tareas son más complicadas, ocupan más espacio de código, y tomar más ciclos. También se requieren al menos una interrupción. Que es a menudo una carga inaceptable para la aplicación.

Los Mutexes se requiere alrededor de estructuras que pueden tener acceso simultáneamente. En un sistema cooperativo, que simplemente no la llame TASK_YIELD en el medio de lo que debería ser una operación atómica. Este efectos colas, estado global compartido, y creaps en un montón de lugares.

En general, dedicando una tarea para un trabajo en particular, tiene sentido cuando la CPU puede ofrecer apoyo y el trabajo es bastante complicada con suficiente historia dependiente de la operación de que se rompa en un par de separar cada uno de los eventos podría ser engorroso. Este es generalmente el caso cuando el manejo de las comunicaciones del flujo de entrada. Tales cosas son generalmente estatales fuertemente impulsado dependiendo alguna entrada anterior. Por ejemplo, puede ser opcode bytes seguidos por los bytes de datos únicos para cada código de operación. Luego está el problema de estos bytes venir a usted cuando algo se siente como el envío de ellos. Con una tarea de manipulación de la señal de entrada, puede hacer que aparezca en el código de la tarea como si usted va a salir y llegar al siguiente byte.

En general, las tareas son útiles cuando hay un montón de contexto de estado. Las tareas son básicamente máquinas de estado con el PC con la variable de estado.

Muchas cosas que un micro que tiene que hacer puede ser expresada como la de responder a un conjunto de eventos. Como resultado, generalmente tengo un main event loop. Esto comprueba cada posible evento en la secuencia, luego salta de nuevo a la parte superior y lo hace todo de nuevo. Cuando el manejo de un evento que lleva más de un par de ciclos, me suele saltar de nuevo al comienzo del bucle de eventos después de manejar el evento. En efecto esto significa que los acontecimientos tienen una prioridad implícita basada en donde se comprueba en la lista. En muchos sistemas simples, esto es lo suficientemente bueno.

A veces un poco de las tareas más complicadas. Estos a menudo se puede descomponer en una secuencia de un pequeño número de cosas que hacer. Usted puede utilizar indicadores internos como los eventos en esos casos. He hecho este tipo de cosas muchas veces en la final y baja de Fotos.

Si usted tiene el evento básica de la estructura que el anterior, pero también tiene que responder a un comando de transmitir a través de la UART, por ejemplo, es muy útil tener una tarea independiente de la manija de la recibida de la UART corriente. Algunos microcontroladores de hardware limitada de recursos para la multitarea, como una PIC de 16 que no puede leer o escribir su propia pila de llamadas. En tales casos, yo uso un lo que yo llamo un pseudo-tarea para la UART procesador de comandos. El evento principal de bucle todavía encarga de todo lo demás, pero uno de sus eventos a manejar es que una nueva bytes fue recibido por la UART. En ese caso, salta a una rutina que se ejecuta este pseudo-tarea. La UART del módulo de comando contiene el código de la tarea, y la ejecución de la dirección y un par de valores de registro de la tarea se guardan en la memoria RAM en ese módulo. El código saltó a por el bucle de eventos guarda los registros, las cargas de la guarda de la tarea de los registros, y salta a la tarea de reiniciar dirección. El código de la tarea invoca un RENDIMIENTO macro que hace a la inversa, que, eventualmente, salta de nuevo al inicio del main event loop. En algunos casos, el principal evento de bucle se ejecuta el pseudo-tarea una vez por pasar, generalmente en la parte inferior para hacer una baja prioridad evento.

En un PIC de 18 años y más, yo uso una verdadera cooperativa de tareas del sistema desde la pila de llamadas es legible y escribible por el firmware. En estos sistemas, el reinicio de la dirección, un par de otras piezas de estado, y los datos del puntero de pila se guardan en un buffer de memoria para cada tarea. Permitir que todas las otras tareas que se ejecutan una vez, una tarea llamadas TASK_YIELD. Esto ahorra el actual estado de las tareas, se ve a través de la lista para la siguiente tarea, carga su estado, luego lo ejecuta.

En esta arquitectura, el principal evento de bucle es otra tarea, con una llamada a TASK_YIELD en la parte superior del bucle.

Todos mis multitarea código de Fotos está disponible de forma gratuita. A ver, instalar el PIC Desarrollo de Herramientas de liberación en http://www.embedinc.com/pic/dload.htm. Busque los archivos con la "tarea" en sus nombres en la FUENTE > PIC directorio para la 8 bits de Fotos, y la FUENTE > DSPIC directorio para la 16 bits de Fotos.

1voto

Darren Newton Puntos 835

Editar: (Yo voy a dejar mi anterior post de abajo; tal vez pueda ayudar a alguien algún día.)

Los sistemas operativos multitarea de cualquier tipo y Rutinas de Servicio de Interrupción no son - o no deberían estar compitiendo con las arquitecturas de sistema. Que se supone para los diferentes trabajos en los diferentes niveles del sistema. Las interrupciones son realmente la intención de breves secuencias de código para manejar las tareas inmediatas como reiniciar el dispositivo, posiblemente de votación no interrumpir a los dispositivos de cronometraje en software, etc. Se asume generalmente que el fondo va a hacer que cualquier transformación que ya no es el tiempo crítico de inmediato después de la necesidades han sido satisfechas. Si todo lo que tienes que hacer es reiniciar un contador de tiempo y alternar un LED por pulsos o de otro dispositivo, el ISR en general pueden hacer todo en el primer plano de forma segura. De lo contrario, se debe informar al fondo (mediante el establecimiento de una bandera, o la cola de un mensaje) que algo hay que hacer, y liberar el procesador.

He visto muy simple programa de estructuras cuyo fondo bucle es sólo un bucle de inactividad: for(;;){ ; }. Todo el trabajo fue realizado en el temporizador de ISR. Esto puede funcionar cuando el programa necesita para repetir algunas de operación constante que está garantizado para terminar en menos de un período de tiempo; determinados tipos de procesamiento de la señal vienen a la mente.

Yo, personalmente, escribir Informes que limpiar un salir, y dejar que el fondo tome encima de cualquier otra cosa que se necesita hacer, incluso si es tan simple como multiplicar y añade que se podría hacer en una fracción de un período de tiempo. Por qué? Algún día, voy a conseguir la brillante idea de añadir otro "simple" función de mi programa, y "diablos, voy a tomar un corto ISR a hacerlo", y de repente mi anteriormente simple de la arquitectura crece algunas interacciones que yo no había planeado y ocurren de forma incoherente. Esos no son muy divertido de depuración.


(Previamente publicado comparación de dos tipos de multi-tasking)

La conmutación de tareas: Preventiva MT encarga de la conmutación de tareas para usted, incluyendo asegurándose de que no hay hilo obtiene de la CPU de hambre, y de alta prioridad en los hilos de llegar a correr tan pronto como estén listos. Cooperativa MT requiere que el programador para asegurarse de que ningún hilo mantiene el procesador por mucho tiempo de un tiempo. También tendrás que decidir cuánto tiempo es demasiado largo. Eso también significa que significa que cada vez que se modifica el código, tendrás que ser consciente de si cualquier segmento de código supera ahora el tiempo cuántico.

Proteger a los no-operaciones atómicas: Con un PMT, usted tendrá que asegurarse de que el hilo de los swaps no se producen en el medio de operaciones que no debe dividirse. La lectura/escritura de ciertas dispositivo de registro de pares que deben ser manejados en un orden determinado o dentro de una cantidad máxima de tiempo, por ejemplo. Con CMT es muy fácil - simplemente no ceder el procesador en el medio de una operación de ese tipo.

Depuración: En general es más sencillo con la CMT, ya que el plan de cuándo/dónde hilo interruptores va a ocurrir. Las condiciones de carrera entre los hilos y los errores relacionada con el hilo de la seguridad de las operaciones con un PMT son particularmente difíciles de depurar porque el hilo de los cambios son probabilísticos, por lo que no se puede repetir.

La comprensión del código: Hilos escrito para un PMT son bastante escrito como si pudieran estar solos. Hilos escrito por un CMT se escriben como segmentos y dependiendo de la estructura de programa que usted elija, puede ser más difícil para un lector a seguir.

El uso no seguro para subprocesos código de biblioteca: Se deberá verificar que cada una de las bibliotecas de la función de llamar en virtud de un PMT thread-safe. printf() y scanf() y sus variantes son casi siempre no es thread-safe. Con un informe de la CMT, usted sabrá que no el hilo se producirá el cambio, excepto cuando específicamente el rendimiento del procesador.

Una máquina de estados finitos-sistema de control de un dispositivo mecánico y/o seguimiento de los eventos externos a menudo son buenos candidatos para la CMT, ya que en cada evento, no hay mucho que hacer - iniciar o detener un motor, se establece un indicador, elija el siguiente estado, etc. Por lo tanto, de cambio de estado de las funciones son inherentemente breve.

Un enfoque híbrido puede funcionar muy bien en estos tipos de sistemas: CMT para administrar el estado de la máquina (y por lo tanto, la mayoría del hardware) que se ejecuta como un hilo, y uno o dos más subprocesos para hacer cualquier dejan de ejecutar los cálculos se inició por un cambio de estado.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X