Toda la verdad sobre RTOS. Artículo # 19. Semáforos: introducción y servicios básicos.



Los semáforos se mencionaron en uno de los artículos anteriores (# 5). Su tarea principal es controlar el acceso a los recursos.

Artículos anteriores de la serie:
Artículo # 18. Grupos de banderas de eventos: servicios auxiliares y estructuras de datos
Artículo # 17. Grupos de banderas de eventos: Introducción y servicios básicos
Artículo # 16. Señales
Artículo # 15. Particiones de memoria: servicios y estructuras de datos
Artículo # 14. Secciones de memoria: introducción y servicios básicos.
Artículo 13. Estructuras de datos de tareas y llamadas de API no compatibles
Artículo # 12. Servicios para trabajar con tareas.
Artículo # 11. Tareas: configuración e introducción a la API
Artículo # 10. Programador: funciones avanzadas y preservación del contexto
Artículo # 9. Programador: implementación
Artículo # 8. Nucleus SE: diseño interno y despliegue
Artículo # 7. Núcleo SE: Introducción
Artículo # 6. Otros servicios RTOS
Artículo # 5. Interacción de tareas y sincronización
Artículo # 4. Tareas, cambio de contexto e interrupciones
Artículo # 3. Tareas y planificación
Artículo # 2. RTOS: estructura y modo en tiempo real
Artículo # 1. RTOS: introducción.

Usando semáforos


En Nucleus SE, los semáforos se definen en la etapa de ensamblaje. Una aplicación puede tener hasta 16 semáforos. Si no se especifican los semáforos, el código de las llamadas de servicio y las estructuras de datos no se incluyen en la aplicación.

Un semáforo es un contador del tipo U8 , cuyo acceso se controla de tal manera que varias tareas pueden usarlo. Una tarea puede disminuir el valor del contador de semáforo (captura) o aumentarlo (liberar). Intentar capturar un semáforo con un valor de cero puede provocar un error o una suspensión de la tarea, dependiendo de los parámetros de llamada API seleccionados y la configuración de Nucleus SE.

Configurar semáforos


Número de semáforos


Al igual que con la mayoría de los objetos de Nucleus SE, la configuración de los semáforos está determinada por las directivas #define en nuse_config.h . El parámetro principal es NUSE_SEMAPHORE_NUMBER , que determina el número de semáforos en la aplicación. De forma predeterminada, el parámetro se establece en 0 (los semáforos no se usan en la aplicación) y puede tomar cualquier valor hasta 16. Un valor incorrecto generará un error de compilación, que se generará al ingresar nuse_config_check.h (este archivo se incluye en nuse_config.c , lo que significa que se compila junto con este módulo), como resultado, se activará la directiva #error .

Elegir un valor distinto de cero sirve como activador principal para los semáforos. Este parámetro se utiliza al definir estructuras de datos y su tamaño depende de su valor (para más detalles, consulte más adelante en este artículo). Además, un valor distinto de cero activa la configuración de la API.

Activar llamadas API


Cada función API (llamada de utilidad) en Nucleus SE se activa mediante la directiva #define en nuse_config.h . Para semáforos, estos incluyen:

NUSE_SEMAPHORE_OBTAIN
NUSE_SEMAPHORE_RELEASE
NUSE_SEMAPHORE_RESET
NUSE_SEMAPHORE_INFORMATION
NUSE_SEMAPHORE_COUNT


Por defecto, están configurados en FALSO , deshabilitando así cada llamada de servicio y bloqueando la inclusión de código que los implementa. Para configurar los semáforos, debe seleccionar las llamadas API necesarias y establecer las directivas correspondientes en TRUE .

Lo siguiente es un extracto del archivo predeterminado nuse_config.h .

 #define NUSE_SEMAPHORE_NUMBER 0 /* Number of semaphores in the system - 0-16 */ #define NUSE_SEMAPHORE_OBTAIN FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RELEASE FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RESET FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_INFORMATION FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_COUNT FALSE /* Service call enabler */ 

Una función de API activada si no hay semáforos en la aplicación dará lugar a un error de compilación (excepto NUSE_Semaphore_Count () , que siempre está habilitado). Si su código usa una llamada API que no se ha activado, se producirá un error de diseño porque el código de implementación no se incluyó en la aplicación.

Llamadas de semáforo de utilidad


Nucleus RTOS admite ocho llamadas de servicio que proporcionan la siguiente funcionalidad:

  • Captura de semáforo. Nucleus SE se implementa en la función NUSE_Semaphore_Obtain () .
  • Suelte el semáforo. En Nucleus SE, se implementa en la función NUSE_Semaphore_Release () .
  • Devolver el semáforo a un estado no utilizado con la liberación de todas las tareas pausadas (reinicio). Nucleus SE se implementa en NUSE_Semaphore_Reset () .
  • Proporcionar información sobre un semáforo específico. Nucleus SE se implementa en NUSE_Semaphore_Information () .
  • Devuelve el número de semáforos configurados en la aplicación. Nucleus SE implementado en NUSE_Semaphore_Count () .
  • Agregar un nuevo semáforo a la aplicación. Nucleus SE no está implementado.
  • Eliminando el semáforo de la aplicación. Nucleus SE no está implementado.
  • Devolver punteros a todos los semáforos. Nucleus SE no está implementado.

La implementación de cada llamada de servicio se describe en detalle a continuación.

Llamadas de servicios públicos para capturar y liberar semáforos


Las operaciones fundamentales que se pueden realizar en semáforos son capturar y liberar (disminución y aumento de valor). Nucleus RTOS y Nucleus SE proporcionan dos llamadas API básicas para estas operaciones.

Captura de semáforo


La llamada a la utilidad Nucleus RTOS para capturar un semáforo es muy flexible y le permite pausar tareas implícitamente o con un tiempo de espera específico si la operación no se puede realizar en este momento, por ejemplo, si intenta capturar un semáforo con un valor cero. Nucleus SE proporciona las mismas características, solo la pausa de tareas es opcional y no se implementa un tiempo de espera.

Desafío para capturar semáforos en Nucleus RTOS
Prototipo de llamada de servicio:
ESTADO NU_Obtain_Semaphore (NU_SEMAPHORE * semaphore, UNSIGNED suspend);

Parámetros:

semáforo : puntero al bloque de control de semáforo proporcionado por el usuario;
suspender : parámetro de suspensión de la tarea, puede tomar los valores NU_NO_SUSPEND o NU_SUSPEND , así como el valor de tiempo de espera.

Valor de retorno:

NU_SUCCESS : la llamada se completó correctamente;
NU_UNAVAILABLE : el semáforo tenía un valor nulo;
NU_INVALID_SEMAPHORE : puntero no válido a un semáforo;
NU_INVALID_SUSPEND : intenta pausar desde un hilo no relacionado con la tarea;
NU_SEMAPHORE_WAS_RESET : el semáforo se restableció mientras se suspendía la tarea.

Desafío para capturar semáforos en Nucleus SE
Esta llamada a la API admite la funcionalidad principal de la API Nucleus RTOS.

Prototipo de llamada de servicio:

ESTADO NUSE_Semaphore_Obtain (NUSE_SEMAPHORE semaphore, U8 suspend);

Parámetros:

semáforo - índice (ID) del semáforo utilizado;
suspender : parámetro de suspensión de la tarea, puede ser NUSE_NO_SUSPEND o NUSE_SUSPEND .

Valor de retorno:

NUSE_SUCCESS : la llamada se completó correctamente;
NUSE_UNAVAILABLE : el semáforo tenía un valor nulo;
NUSE_INVALID_SEMAPHORE : índice de semáforo no válido;
NUSE_INVALID_SUSPEND : un intento de pausa desde un hilo no relacionado con la tarea o cuando la funcionalidad de bloqueo de la API está deshabilitada;
NUSE_SEMAPHORE_WAS_RESET : el semáforo se restableció mientras se suspendía la tarea;

Implementación de captura de semáforos en Nucleus SE
La versión del código de función NUSE_Semaphore_Obtain () (después de verificar los parámetros) se selecciona mediante compilación condicional dependiendo de si el soporte para tareas de bloqueo (pausa) está activado o no. Considera ambas opciones.

Si el bloqueo no está activado, la lógica de esta llamada a la API es bastante simple:

 if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; } else /* semaphore unavailable */ { return_value = NUSE_UNAVAILABLE; } 

Se verifica el valor del contador de semáforos y, si no es igual a cero, disminuye.

Si se activa el bloqueo de tareas, la lógica se vuelve más compleja:

 do { if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* semaphore unavailable */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_UNAVAILABLE; } else { /* block task */ NUSE_Semaphore_Blocking_Count[semaphore]++; NUSE_Suspend_Task(NUSE_Task_Active, semaphore << 4) | NUSE_SEMAPHORE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND); 

Algunas aclaraciones pueden ser útiles.

El código se coloca en un bucle do ... while , que se ejecuta mientras el parámetro de suspensión es NUSE_SUSPEND .

Si el semáforo tiene un valor distinto de cero, disminuye. La variable de suspensión se establece en NUSE_NO_SUSPEND , y la llamada a la API finaliza y devuelve NUSE_SUCESS .

Si el semáforo es nulo y la variable de suspensión se establece en NUSE_NO_SUSPEND , la llamada API devuelve NUSE_UNAVAILABLE . Si la suspensión se estableció en NUSE_SUSPEND , la tarea se detiene. Una vez que se completa la llamada (por ejemplo, cuando se reanuda la tarea), si el valor de retorno es NUSE_SUCCESS (que indica que la tarea se reanudó después de liberar el semáforo y no después del reinicio), el ciclo comienza desde el principio.

Lanzamiento de semáforo


La llamada de utilidad a la API Nucleus RTOS para liberar el semáforo es bastante simple: el valor del contador de semáforo aumenta y se devuelve un mensaje de éxito. Nucleus SE ofrece las mismas características, pero con comprobación de desbordamiento adicional.

Desafío para liberar semáforos en Nucleus RTOS
Prototipo de llamada de servicio:

ESTADO NU_Release_Semaphore (NU_SEMAPHORE * semaphore);

Parámetros:

semáforo : un puntero a un bloque de control de semáforo proporcionado por el usuario.

Valor de retorno:

NU_SUCCESS : la llamada se completó correctamente;
NU_INVALID_SEMAPHORE : puntero de semáforo no válido .

Desafío para liberar el semáforo en Nucleus SE
Esta llamada a la API admite la funcionalidad principal de la API Nucleus RTOS.

Prototipo de llamada de servicio:

ESTADO NUSE_Semaphore_Release (semáforo NUSE_SEMAPHORE);

Parámetros:

semáforo : el índice (ID) del semáforo liberado.

Valor de retorno:

NUSE_SUCCESS : la llamada se completó correctamente;
NUSE_INVALID_SEMAPHORE : índice de semáforo no válido;
NUSE_UNAVAILABLE : el semáforo tiene un valor de 255 y no se puede aumentar.

Implementación de lanzamiento de semáforo en Nucleus SE
El código de función NUSE_Semaphore_Release () (después de verificar los parámetros) es común, independientemente de si el bloqueo de tareas está activado o no. Se verifica el valor del contador de semáforos y, si es inferior a 255, aumenta.

Se selecciona más código utilizando la compilación condicional si se activa el soporte para llamadas de bloqueo de API (suspensión de tareas):

 NUSE_CS_Enter(); if (NUSE_Semaphore_Counter[semaphore] < 255) { NUSE_Semaphore_Counter[semaphore]++; return_value = NUSE_SUCCESS; #if NUSE_BLOCKING_ENABLE if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; /* check whether a task is blocked */ /* on this semaphore */ NUSE_Semaphore_Blocking_Count[semaphore]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } #endif } else { return_value = NUSE_UNAVAILABLE; } NUSE_CS_Exit(); return return_value; 

Si se suspende alguna tarea en este semáforo, se reanuda la primera de ellas.

El siguiente artículo describirá llamadas API adicionales asociadas con semáforos y sus estructuras de datos.

Sobre el autor: Colin Walls ha trabajado en la industria electrónica durante más de treinta años, dedicando la mayor parte de su tiempo al firmware. Ahora es ingeniero de firmware en Mentor Embedded (una división de Mentor Graphics). Colin Walls a menudo habla en conferencias y seminarios, autor de numerosos artículos técnicos y dos libros sobre firmware. Vive en el Reino Unido. Blog profesional de Colin , correo electrónico: colin_walls@mentor.com.

Source: https://habr.com/ru/post/es429156/


All Articles