
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 datosArtículo # 17. Grupos de banderas de eventos: Introducción y servicios básicosArtículo # 16. SeñalesArtículo # 15. Particiones de memoria: servicios y estructuras de datosArtículo # 14. Secciones de memoria: introducción y servicios básicos.Artículo 13. Estructuras de datos de tareas y llamadas de API no compatiblesArtículo # 12. Servicios para trabajar con tareas.Artículo # 11. Tareas: configuración e introducción a la APIArtículo # 10. Programador: funciones avanzadas y preservación del contextoArtículo # 9. Programador: implementaciónArtículo # 8. Nucleus SE: diseño interno y despliegueArtículo # 7. Núcleo SE: IntroducciónArtículo # 6. Otros servicios RTOSArtículo # 5. Interacción de tareas y sincronizaciónArtículo # 4. Tareas, cambio de contexto e interrupcionesArtículo # 3. Tareas y planificaciónArtí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 #define NUSE_SEMAPHORE_OBTAIN FALSE #define NUSE_SEMAPHORE_RELEASE FALSE #define NUSE_SEMAPHORE_RESET FALSE #define NUSE_SEMAPHORE_INFORMATION FALSE #define NUSE_SEMAPHORE_COUNT FALSE
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 RTOSPrototipo 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 SEEsta 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 SELa 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 RTOSPrototipo 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 SEEsta 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 SEEl 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; 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.