RT-Thread Mutex (notas de estudio)

Este artículo hace referencia a [Wildfire EmbedFire] "Implementación de kernel de subprocesos RT y desarrollo de aplicaciones: basado en STM32", que solo se usa como una nota de estudio personal. Para obtener contenido y pasos más detallados, consulte el texto original (puede descargarlo desde el Centro de descarga de datos de Wildfire)

Conceptos básicos de Mutexes

Mutex, también conocido como semáforo mutuamente excluyente, es un semáforo binario especial. A diferencia de los semáforos, admite la propiedad de exclusión mutua, el acceso recursivo y funciones que evitan la inversión de prioridad.
Solo hay dos estados de un mutex, desbloqueado o bloqueado (dos valores de estado). Cuando un subproceso lo retiene, el mutex se bloquea y el subproceso se apropia de él. En cambio, cuando el subproceso lo libera, el mutex se desbloquea y pierde su propiedad. Cuando un subproceso tiene un mutex, otros subprocesos no podrán desbloquearlo o mantenerlo, y el subproceso que contiene el mutex podrá adquirir el bloqueo nuevamente sin ser suspendido. Esta característica es muy diferente del semáforo binario general.En el semáforo, debido a que no hay una instancia, el subproceso recursivamente se suspenderá activamente (eventualmente formará un interbloqueo).

——Manual chino oficial de RT-Thread

Mecanismo de herencia de prioridad de exclusión mutua

Otro problema potencial causado por el uso de semáforos es el cambio de prioridad de subprocesos. El llamado problema de inversión de prioridad es que cuando un subproceso de alta prioridad intenta acceder a un recurso compartido a través del mecanismo de semáforo, si el semáforo ya está en manos de un subproceso de baja prioridad, y el subproceso de baja prioridad puede ser utilizado por otros algunos subprocesos de prioridad media se reemplazan, lo que provoca que los subprocesos de prioridad alta sean bloqueados por muchos subprocesos de prioridad más baja, lo que dificulta garantizar el rendimiento en tiempo real.

El protocolo de herencia de prioridad significa que la prioridad de un subproceso de baja prioridad que ocupa un determinado recurso se incrementa para ser igual a la prioridad del subproceso con la prioridad más alta entre todos los subprocesos que esperan el recurso, y luego se ejecuta, y cuando esto Cuando el subproceso de baja prioridad libera el recurso, la prioridad vuelve a la configuración inicial. Por lo tanto, los subprocesos con prioridades heredadas evitan la preferencia de los recursos del sistema por parte de cualquier subproceso de prioridad intermedia.

• Advertencia: Libere el mutex lo antes posible después de adquirirlo y no cambie la prioridad del subproceso que contiene el mutex mientras se mantiene.

——Manual chino oficial de RT-Thread

Escenarios de aplicaciones de exclusión mutua

Las exclusiones mutuas se aplican a:

  • Una situación en la que un subproceso puede adquirir un mutex varias veces. Esto puede evitar el interbloqueo causado por el mismo subproceso que se mantiene varias veces recursivamente;
  • Una situación que puede causar inversión de prioridad.

Ejemplo típico: durante la comunicación en serie, dado que solo hay un recurso de hardware, si es necesario enviar dos subprocesos al mismo tiempo, se debe agregar un bloqueo de exclusión mutua.

Nota: No se pueden usar mutexes en funciones de servicio de interrupción.

Cómo funcionan las exclusiones mutuas

Un mutex es equivalente a una clave. Si dos subprocesos desean adquirir el mismo recurso, primero deben adquirir la "clave". Después de que el subproceso actual libera la "clave", el siguiente subproceso puede continuar accediendo al recurso.

Fuente de la imagen: "Práctica de desarrollo de aplicaciones e implementación del núcleo RT-Thread"

inserte la descripción de la imagen aquí

Bloque de control de exclusión mutua

struct rt_mutex
{
    
    
    struct rt_ipc_object parent;                        /**< 继承自ipc_object类 */

    rt_uint16_t          value;                         /**< 互斥量的值 */

    rt_uint8_t           original_priority;             /**< 持有线程的原始优先级 */
    rt_uint8_t           hold;                          /**< 持有线程的持有次数 */

    struct rt_thread    *owner;                         /**< 当前拥有互斥量的线程 */
};

Introducción a la interfaz de la función Mutex

Crear mutex rt_mutex_create()

rt_mutex_t rt_mutex_create(const char* nombre, indicador rt_uint8_t);

Puede llamar a la función rt_mutex_create para crear una exclusión mutua cuyo nombre se especifica por nombre. El mutex creado tiene diferentes significados debido a las diferentes banderas especificadas: Para un objeto IPC creado usando la bandera de prioridad PRIO, cuando varios subprocesos esperan recursos, el subproceso con mayor prioridad dará prioridad a la obtención de recursos. El objeto IPC creado con el indicador FIFO primero en entrar, primero en salir obtendrá los recursos por orden de llegada cuando varios subprocesos esperan recursos.

parámetro describir
nombre nombre mutex
bandera bandera de exclusión mutua

Eliminar mutex rt_mutex_delete()

rt_err_t rt_mutex_delete(rt_mutex_t exclusión mutua);

Cuando se elimina un mutex, todos los subprocesos que esperan este mutex se activarán y el valor de retorno obtenido por los subprocesos en espera es -RT_ERROR. Luego, el sistema elimina la exclusión mutua de la lista vinculada del administrador de objetos del kernel y libera el espacio de memoria ocupado por la exclusión mutua.

parámetro describir
exclusión mutua Un identificador para el objeto mutex

Inicializar mutex rt_mutex_init()

rt_err_t rt_mutex_init(rt_mutex_t mutex, const char* name, rt_uint8_t flag);

Al usar esta interfaz de función, debe especificar el identificador del objeto de exclusión mutua (es decir, el puntero al bloque de control de exclusión mutua), el nombre de la exclusión mutua y la bandera de la exclusión mutua. Las banderas Mutex pueden usar las banderas mencionadas en la función Create Mutex anterior.

parámetro describir
exclusión mutua Un identificador para el objeto mutex
nombre nombre mutex
bandera bandera de exclusión mutua

Liberación de exclusión mutua rt_mutex_release()

rt_err_t rt_mutex_release(rt_mutex_t exclusión mutua);

Cuando se usa esta interfaz de función, solo el subproceso que ya tiene el control del mutex puede liberarlo. Cada vez que se libera el mutex, su conteo de retención se reduce en 1. Cuando el conteo de retención del mutex es cero (es decir, el subproceso de retención ha liberado todas las operaciones de retención), se vuelve disponible y los subprocesos que esperan en el semáforo se despertarán.

parámetro describir
exclusión mutua Un identificador para el objeto mutex

Adquisición de exclusión mutua rt_mutex_take()

rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t tiempo);

Si el mutex no está controlado por otro subproceso, el subproceso que solicitó el mutex adquirirá con éxito el mutex. Si el mutex ya está controlado por el subproceso actual, el conteo de retención del mutex se incrementa en 1 y el subproceso actual no suspenderá la espera. Si el mutex ya está ocupado por otro subproceso, el subproceso actual suspende la espera en el mutex hasta que el otro subproceso lo libere o espere más tiempo que el tiempo de espera especificado.

parámetro describir
exclusión mutua Un identificador para el objeto mutex
tiempo El tiempo de espera especificado, en OS Tick

Consideraciones de uso de exclusión mutua

Precauciones:

  1. Dos subprocesos no pueden contener el mismo mutex al mismo tiempo.
  2. Mutexes no se puede utilizar en las rutinas de servicio de interrupción.
  3. Para garantizar el rendimiento en tiempo real de RT-Thread, se debe evitar que el mutex se adquiera y no se libere durante mucho tiempo.
  4. Las funciones como rt_thread_control() (modificar la prioridad del subproceso) no se pueden llamar en un subproceso mientras tiene un mutex.

experimento mutex

Para usar un mutex, debe rtconfig.habrir la configuración correspondiente en .

inserte la descripción de la imagen aquí
Este experimento se refiere al documento chino oficial. El experimento creará 3 subprocesos dinámicos. Después de que el subproceso de baja prioridad obtenga la exclusión mutua, se evaluará si su prioridad se ha ajustado a la prioridad más alta entre las prioridades del subproceso en espera.
Las prioridades de los subprocesos 1, 2 y 3 son alta, media y baja, respectivamente. El subproceso 3 retiene el mutex primero y luego deja que el subproceso 2 lo adquiera. En este momento, el subproceso 2 debe ajustarse a la misma prioridad que el subproceso 2. Este proceso de comprobación tiene lugar en el subproceso 1.

  • Nota: este subproceso de experimento 3 tiene un largo retraso después de adquirir el mutex, y este uso debe evitarse en el uso real
#include "board.h"
#include "rtthread.h"

// 定义线程控制块指针
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;
static rt_thread_t tid3 = RT_NULL;

// 定义互斥量控制块
static rt_mutex_t test_mux = RT_NULL;


/******************************************************************************
* @ 函数名  : thread1_entry
* @ 功  能  : 线程1入口
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void thread1_entry(void *parameter)
{
    
    

	// 让低优先级线程先运行
	rt_thread_delay(10);
	
	// 此时线程3持有互斥量,并线程2等待互斥量
	
	// 检查线程2与线程3的优先级情况
	if(tid2->current_priority != tid3->current_priority)
	{
    
    
		// 优先级不相同,测试失败
		rt_kprintf("线程3优先级未变化,测试失败!\n");
	}
	else
		rt_kprintf("线程3优先级变为%d,测试成功!\n");
		
	while(1)
	{
    
    
		rt_kprintf("线程2优先级:%d\n", tid2->current_priority);
		rt_kprintf("线程3优先级:%d\n\n", tid3->current_priority);
		
		rt_thread_delay(1000);
	}
		
}
	
/******************************************************************************
* @ 函数名  : thread2_entry
* @ 功  能  : 线程2入口
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void thread2_entry(void *parameter)
{
    
    
	rt_err_t result;
	
	// 让低优先级线程3先运行
	rt_thread_delay(5);
	
	while(1)
	{
    
    
		// 试图获取互斥锁,此时线程3持有互斥锁,应把线程3优先级提升为线程2优先级
		result = rt_mutex_take(test_mux, RT_WAITING_FOREVER);
		
		if(result == RT_EOK)
		{
    
    
			rt_kprintf("线程2获取到互斥锁,且已释放\n");
			// 释放互斥锁
			rt_mutex_release(test_mux);
			rt_thread_delay(800);
		}
	}
}
		
/******************************************************************************
* @ 函数名  : thread3_entry
* @ 功  能  : 线程3入口
* @ 参  数  : parameter 外部传入的参数
* @ 返回值  : 无
******************************************************************************/
static void thread3_entry(void *parameter)
{
    
    
	rt_err_t result;
	
	while(1)
	{
    
    
		// 获取互斥锁(测试连续获取两次)
		result = rt_mutex_take(test_mux, RT_WAITING_FOREVER);
		result = rt_mutex_take(test_mux, RT_WAITING_FOREVER);
		
		if(result == RT_EOK)
		{
    
    
			rt_kprintf("线程3获取到互斥锁\n");
		}
		
		// 做一个长时间的挂起(实际使用时非常不建议这样用)
		rt_thread_delay(1500);
		
		rt_kprintf("线程3释放互斥锁\n");
		
		// 释放互斥锁
		rt_mutex_release(test_mux);
		rt_mutex_release(test_mux);
	}
}
		

int main(void)
{
    
    
	// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成
	
	// 创建一个互斥量
	test_mux =                                    // 互斥量控制块指针
	rt_mutex_create("test_mux",                   // 互斥量名字                       // 互斥量初始值
	                RT_IPC_FLAG_FIFO);            // FIFO队列模式(先进先出)
	
	if(test_mux != RT_NULL)
		rt_kprintf("互斥量创建成功!\n");

	// 创建线程1
	tid1 =                                        // 线程控制块指针
	rt_thread_create("tid1",                      // 线程名字
	                thread1_entry,                // 线程入口函数
	                RT_NULL,                      // 入口函数参数
	                255,                          // 线程栈大小
				    2,                            // 线程优先级
					10);                          // 线程时间片
	
	
	// 开启线程调度
	if(tid1 != RT_NULL)
		rt_thread_startup(tid1);
	else
		return -1;
	
	
	// 创建线程2
	tid2 =                                        // 线程控制块指针
	rt_thread_create("tid2",                      // 线程名字
	                thread2_entry,                // 线程入口函数
	                RT_NULL,                      // 入口函数参数
	                255,                          // 线程栈大小
				    3,                            // 线程优先级
					10);                          // 线程时间片
	
	
	// 开启线程调度
	if(tid2 != RT_NULL)
		rt_thread_startup(tid2);
	else
		return -1;
	
	// 创建线程3
	tid3 =                                        // 线程控制块指针
	rt_thread_create("tid3",                      // 线程名字
	                thread3_entry,                // 线程入口函数
	                RT_NULL,                      // 入口函数参数
	                255,                          // 线程栈大小
				    4,                            // 线程优先级
					10);                          // 线程时间片
	
	
	// 开启线程调度
	if(tid3 != RT_NULL)
		rt_thread_startup(tid3);
	else
		return -1;
							
}


Fenómenos experimentales

Cuando el subproceso 3 con la prioridad más baja retiene la exclusión mutua y el subproceso 2 también comienza a adquirir la exclusión mutua, la prioridad del subproceso 3 se eleva a la prioridad del subproceso 2.

Cuando el subproceso 2 libera la exclusión mutua y usa la función de retraso para suspender, el subproceso 3 adquiere la exclusión mutua nuevamente, pero debido a que el subproceso 2 no está en la cola de espera de exclusión mutua, la prioridad del subproceso 3 permanece sin cambios.

Cuando finaliza el retraso del subproceso 2, continúa intentando adquirir el mutex.En este momento, la prioridad del subproceso 3 se eleva a la prioridad del subproceso 2 nuevamente.

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_43772810/article/details/123786613
Recomendado
Clasificación