RT-Thread学习笔记——信号量

2023-05-16

前言

本文讲RT-Thread的线程间同步之信号量,包括为什么要进行线程间同步、信号量创建与删除、信号量获取与释放以及基于STM32的二值信号量示例和计算型信号量示例,采用RTT&正点原子联合出品潘多拉开发板进行实验。

一、线程间同步

1、什么是线程间同步?

同步是指按预定的先后次序进行运行,线程同步是指多个线程通过特定的机制来控制线程之间的执行顺序,也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间将是无序的。 

2、为什么要进行线程间同步?

例如一项工作中的两个线程:一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递: 

线程间数据传递示意图
线程间数据传递示意图(来源RT-Thread编程指南)

如果对共享内存的访问不是排他性的,那么各个线程间可能同时访问它,这将引起数据一致性的问题。例如,在显示线程试图显示数据之前,接收线程还未完成数据的写入,那么显示将包含不同时间采样的数据,造成显示数据的错乱。将传感器数据写入到共享内存块的接收线程 #1 和将传感器数据从共享内存块中读出的线程 #2 都会访问同一块内存。为了防止出现数据的差错,两个线程访问的动作必须是互斥进行的,应该是在一个线程对共享内存块操作完成后,才允许另一个线程去操作,这样,接收线程 #1 与显示线程 #2 才能正常配合,使此项工作正确地执行。 

 

二、信号量

信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的含义分两种情况:(1) 0,表示没有积累下来的 release 释放信号量操作,且有可能有在此信号量上阻塞的线程。(2)正值,表示有一个或多个 release 释放信号量操作。

1、 互斥为目的的信号量:用作互斥时,信号量创建后可用信号量个数应该是满的,线程在需要使用临界资源时,先获取信号量,使其变空,这样其他线程需要使用临界资源时就会因为无法获取信号量而进入阻塞,从而保证了临界资源的安全。

2、同步为目的的信号量:用作同步时,信号量在创建后被置为空,线程 1 取信号量而阻塞,线程 2 在某种条件发生后,释放信号量,于是线程 1 得以进入就绪态,如果线程 1 的优先级是最高的,那么就会立即切换线程,从而达到了两个线程间的同步。同样的,在中断服务函数中释放信号量,也能达到线程与中断间的同步。

3、创建动态信号量函数:当创建一个信号量时,内核首先创建一个信号量控制块,然后对该控制块进行基本的初始化工作。信号量标志参数flag决定了当信号量不可用时,多个线程等待的排队方式。当选择 RT_IPC_FLAG_FIFO(先进先出)方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择RT_IPC_FLAG_PRIO(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。

rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);

 (1)入口参数:

  name:信号量名称。
  value:信号量初始值。
  flag:信号量标志,它可以取如下数值:RT_IPC_FLAG_FIFO 或RT_IPC_FLAG_PRIO。

(2)返回值

  RT_NULL:创建失败。
  信号量的控制块指针:创建成功。

4、删除动态信号量函数:系统不再使用动态信号量时,可通过删除动态信号量以释放系统资源,如果系统删除该信号量时,有线程正在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程(等待线程的返回值是- RT_ERROR),然后再释放信号量的内存资源。

rt_err_t rt_sem_delete(rt_sem_t sem);

(1)入口参数:

  sem:创建的信号量对象。

(2)返回值:

  RT_EOK:删除成功。

5、创建静态信号量函数:创建静态信号量也就是《RT-Thread编程指南》所讲的初始化信号量。对于静态信号量对象,它的内存空间在编译时期就被编译器分配出来,放在读写数据段或未初始化数据段上,此时使用信号量就不再需要使用 rt_sem_create 接口来创建它,而只需在使用前对它进行初始化即可。

rt_err_t rt_sem_init(rt_sem_t    sem,
                     const char *name,
                     rt_uint32_t value,
                     rt_uint8_t  flag);

(1)入口参数:

 sem:信号量对象的句柄。
 name:信号量名称。
 value:信号量初始值。
 flag:信号量标志,它可以取如下数值:RT_IPC_FLAG_FIFO 或 RT_IPC_FLAG_PRIO。

(2)返回值:

 RT_EOK:初始化成功。

6、删除静态信号量函数:删除静态信号量也就是《RT-Thread编程指南》所讲的脱离信号量,就是让信号量对象从内核对象管理器中脱离。当不再需要静态信号量时,可删除静态信号量,内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中脱离,原来挂起在信号量上的等待线程将获得-RT_ERROR 的返回值。

rt_err_t rt_sem_detach(rt_sem_t sem);

(1)入口参数:

 sem:信号量对象的句柄。

(2)返回值:
 RT_EOK:脱离成功。

7、获取信号量函数:线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的
信号量值会减 1。如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数 time 指定的时间内依然得不到信号量,线程将超时返回,返回值是-RT_ETIMEOUT。

rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);

(1)入口参数

 sem:信号量对象的句柄。
 time:指定的等待时间,单位是操作系统时钟节拍(OS Tick)。

(2)返回值

 RT_EOK:成功获得信号量。
 RT_ETIMEOUT:超时依然未获得信号量。
 RT_ERROR:其他错误。

8、无等待获取信号量:当用户不想在申请的信号量上挂起线程进行等待时,可以使用无等待方式获取信号量,它的作用是和rt_sem_take(sem, 0) 一样的,即当线程申请的信号量资源实例不可用的时候,它不会等待在该信号量上,而是直接返回RT_ETIMEOUT。

rt_err_t rt_sem_trytake(rt_sem_t sem);

(1)入口参数:

  sem:信号量对象的句柄。

(2)返回值:

 RT_EOK:成功获得信号量。
 RT_ETIMEOUT:获取失败。

9、释放信号量函数:释放信号量可以唤醒挂起在该信号量上的线程,当信号量的值等于零时,并且有线程等待这个信号量时,释放信号量将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量,同时将把信号量的值加 1。

rt_err_t rt_sem_release(rt_sem_t sem);

(1)入口参数:

 sem:信号量对象的句柄。

(2)返回值:

 RT_EOK:成功释放信号量。

 

三、二值信号量和计算型信号量

1、二值信号量

因为信号量资源被获取了,信号量值就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0和 1 两种情况的信号量称之为二值信号量。在嵌入式操作系统中二值信号量是线程间、线程与中断间同步的重要手段。

2、二值信号量的运作机制

创建二值信号量,为创建的信号量对象分配内存,并把可用信号量初始化为用户自定义的个数, 二值信号量的最大可用信号量个数为 1。信号量获取,从创建的信号量资源中获取一个信号量,获取成功返回正确。否则线程会等待其它线程释放该信号量,超时时间由用户设定。当线程获取信号量失败时,线程将进入阻塞态,系统将线程挂到该信号量的阻塞列表中。

(1)在二值信号量无效的时候,假如此时有线程获取该信号量的话,那么线程将进入阻塞状态,如下图所示。

信号量无效的时候获取(来源[野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》)
信号量无效的时候获取(来源[野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》)

(2)假如某个时间中断/线程释放了信号量,那么由于获取无效信号量而进入阻塞态的线程将获得信号量并且恢复为就绪态,如下图所示。

中断、线程释放信号量([野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》)
中断、线程释放信号量(来源[野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》)

(3)二值信号量运作机制如下图所示。

二值信号量运作机制
二值信号量运作机制(来源[野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》)

3、计数型信号量

计数型信号量与二值信号量其实都是差不多的,一样用于资源保护,不过计数信号量则允许多个线程获取信号量访问共享资源,但会限制线程的最大数目。访问的线程数达到信号量可支持的最大数目时,会阻塞其他试图获取该信号量的线程,直到有线程释放了信号量。

4、计数型信号量的运作机制

虽然计数信号量允许多个线程访问同一个资源,但是也有限定,比如某个资源限定只能有 3 个线程访问,那么第 4 个线程访问的时候,会因为获取不到信号量而进入阻塞,等到有线程(比如线程 1)释放掉该资源的时候,第 4个线程才能获取到信号量从而进行资源的访问。

计数信号量运作示意图
计数信号量运作示意图(来源[野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》)

 

四、基于STM32的二值信号量示例

前面讲了很多关于信号量的概念知识,光说不练都是假把式,那么接下来我们就使用STM32L475VET6,RTT&正点原子联合出品的潘多拉开发板来进行实际操作。首先进行二值信号量的实验:创建一个信号量,两个线程,信号量值初始化为1,一个线程用于修改公共资源(定义一个全局变量作为公共资源)值,另一个线程用于获取公共资源值并通过FinSH打印处理,通过二值信号量从而实现只有在公共资源先被修改更新之后才能被获取。

1、实现代码

#include "main.h"
#include "board.h"
#include "rtthread.h"
#include "data_typedef.h"
#include "led.h"

int main(void)
{
    binary_semaphore_test();
    return 0;
}

/* 定义线程控制块 */
static rt_thread_t receive_data_thread = RT_NULL;
static rt_thread_t send_data_thread = RT_NULL;
/* 定义二值信号量控制块 */
static rt_sem_t binary_sem = RT_NULL;
/* 公共资源 */
u8 g_value = 1;	

/**************************************************************
函数名称 : send_data_thread_entry
函数功能 : 修改公共资源线程入口函数
输入参数 : parameter:入口参数
返回值  	: 无
备注    : 无
**************************************************************/
void send_data_thread_entry(void *parameter)
{
	while(1) 
	{
		/* 移植等,直到获取到信号量 */
		rt_sem_take(binary_sem, RT_WAITING_FOREVER); /* 等待时间:一直等 */
		if(g_value < 10)
		{
			g_value++;
		}
		else
		{
			g_value = 1;
		}
		rt_sem_release(binary_sem);  /* 释放二值信号量 */
		rt_thread_mdelay(1);
		rt_thread_yield(); 			/* 放弃剩余时间片,进行一次线程切换 */
	}
}

/**************************************************************
函数名称 : receive_data_thread_entry
函数功能 : 获取公共资源线程入口函数
输入参数 : parameter:入口参数
返回值  	 : 无
备注		 : 无
**************************************************************/
void receive_data_thread_entry(void *parameter)
{
	while(1) 
	{
		/* 移植等,直到获取到信号量 */
		rt_sem_take(binary_sem, RT_WAITING_FOREVER); /*  等待时间:一直等 */
		rt_kprintf("g_value:%d\r\n", g_value);
		LED_R(0);
		rt_thread_mdelay(2000);
		LED_R(1);
		rt_thread_mdelay(2000);
		rt_sem_release(binary_sem);/* 释放二值信号量 */
	}
}

/**************************************************************
函数名称 : binary_semaphore_test
函数功能 : 创建二值信号量、创建数据修改线程、接收数据线程
输入参数 : parameter:入口参数
返回值  	 : 无
备注	: 无
**************************************************************/
void binary_semaphore_test(void)
{
    binary_sem = rt_sem_create("binary_sem", /* 信号量名字 */
				1, 	     /* 信号量初始值 */
				RT_IPC_FLAG_FIFO/* 先进先出 */
				);
    if(RT_NULL != binary_sem)/* 创建信号量成功 */
    {
	rt_kprintf("The semaphore was successfully created\r\n");
    }
    else/* 创建信号量失败 */
    {
	rt_kprintf("Failed to create semaphore\r\n");
	return;
    }
    /* 创建接收数据线程 */
    receive_data_thread = rt_thread_create("receive_data",
                                            receive_data_thread_entry,/* 线程入口函数 */
                                            RT_NULL,    /* 入口参数 */
	                                    512,	/* 栈大小 */
	                                    4,		/* 优先级 */
	                                    50		/* 时间片 */
	                                    );
    /* 创建线程成功,则启动线程 */
    if(receive_data_thread != RT_NULL)
    {
        rt_thread_startup(receive_data_thread);
    }
    else
    {
	rt_kprintf("Failed to create receive_data_thread\r\n");
	return;
    }

    /* 创建发送数据线程 */
    send_data_thread = rt_thread_create("send_data",
					send_data_thread_entry,/* 线程入口函数 */
					RT_NULL,  /* 入口参数 */
					512,	  /* 栈大小 */
					3,	  /* 优先级 */
					50        /* 时间片 */
					);    
    /* 创建线程成功,则启动线程 */
    if(send_data_thread != RT_NULL)
    {
        rt_thread_startup(send_data_thread);
    }
    else
    {	
	rt_kprintf("Failed to create send_data_thread\r\n");
	return;
    }
}

2、观察FinSH

开机,可看到每隔2000ms,会打印g_value会改变的值,输入list_sem可查看到当前挂起正在等待信号量的线程。

 

五、基于STM32的计算型信号量示例

使用潘多拉开发板进行计算型信号量的实验,模拟停车场:创建一个信号量,两个线程,其中一个线程用于通过按下按键KEY0获取信号量值,类似获取停车场是否有车位;另外一个线程通过按下按键KEY1用于释放信号量,类似停车场多出车位。当按下按键KEY0后为RGB红灯亮,表示获取信号量失败(信号量值为0),没有停车位,当按下按键KEY0后RGB绿灯亮表示获取信号量成功(信号量值为正值),有停车位。按下按键KEY1后释放信号量来增加停车位。

1、实现代码

#include "main.h"
#include "board.h"
#include "rtthread.h"
#include "data_typedef.h"
#include "led.h"
#include "key.h"
#include "beep.h"

/* 定义线程控制块 */
static rt_thread_t get_parking_place_thread = RT_NULL;
static rt_thread_t release_parking_place_thread = RT_NULL;
/* 定义计算型信号量控制块 */
static rt_sem_t counting_sem = RT_NULL;

void get_parking_place_thread_entry(void *parameter)
{
	rt_err_t res = RT_EOK;
	
	while(1) 
	{
		if(key_scan(0) == KEY0_PRES)
		{
			//res = rt_sem_take(counting_sem, 0);/* 不等待 */
			res = rt_sem_trytake(counting_sem);/* 无等待获取 */
			if(RT_EOK == res)/* 有车位,可停车 */
			{
				LED_R(1);
				LED_G(0);
				rt_kprintf("have parking place\r\n");
			}
			else/* 无车位 */
			{
				LED_R(0);
				LED_G(1);
				rt_kprintf("no parking place\r\n");
			}
		}
		rt_thread_mdelay(1);
	}
}

void release_parking_place_thread_entry(void *parameter)
{
	rt_err_t res = RT_EOK;

	while(1)
	{
		if(key_scan(0) == KEY1_PRES)
		{
			res = rt_sem_release(counting_sem);
			if(RT_EOK == res)
			{
				rt_kprintf("release paring place\r\n");
				BEEP(1);
				rt_thread_mdelay(200);
				BEEP(0);
				rt_thread_mdelay(200);
			}
			else
			{
				rt_kprintf("can not release paring place\r\n");
			}
		}
		rt_thread_mdelay(1);
	}
}

void counting_semaphore_test(void)
{
    counting_sem = rt_sem_create("counting_sem", 	/* 信号量名字 */
        			 0, 			/* 信号量初始值为0 */
				 RT_IPC_FLAG_FIFO	/* 先进先出 */
				 );
    if(RT_NULL != counting_sem)/* 创建信号量成功 */
    {
	rt_kprintf("The counting semaphore was successfully created\r\n");
    }
    else/* 创建信号量失败 */
    {
	rt_kprintf("Failed to create counting semaphore\r\n");
	return;
    }

     /* 创建接收数据线程 */
     get_parking_place_thread = rt_thread_create("get_parking_place",			
				get_parking_place_thread_entry,/* 线程入口函数 */
				RT_NULL,/* 入口参数 */
				512,	/* 栈大小 */
				4,		/* 优先级 */
				50		/* 时间片 */
				);
    /* 创建线程成功,则启动线程 */
    if(get_parking_place_thread != RT_NULL)
    {
        rt_thread_startup(get_parking_place_thread);
    }
    else
    {
	rt_kprintf("Failed to create get_parking_place_thread_entry\r\n");
	return;
    }
            release_parking_place_thread=rt_thread_create("release_parking_place",					                                release_parking_place_thread_entry,	
				                RT_NULL,/* 入口参数 */
				                512,	/* 栈大小 */
				                3,    /* 优先级 */
				                50    /* 时间片 */
				                );
    /* 创建线程成功,则启动线程 */
    if(release_parking_place_thread != RT_NULL)
    {
        rt_thread_startup(release_parking_place_thread);
    }
    else
    {	
	rt_kprintf("Failed to create release_parking_place_thread\r\n");
	return;
    }    
}

2、观察FinSH

(1)开机,按下按键KEY0获取信号量,打印no parking place,同时RGB红灯亮,获取信号量失败,那是因为我们信号量初始值为0,list_sem,counting_sem的值为0:

(2)按下按键KEY1,打印release paring place,释放了信号量,多出一个车位,然后list_sem,counting_sem的值变为1:

(3)再次按下按键KEY1,打印release paring place,释放了信号量:然后list_sem,counting_sem的值变为2:

(4)这时,按下按键KEY0,打印have parking place,获取信号量成功,同时RGB绿灯亮,list_sem,counting_sem的值变为1:

 

六、使用信号量存在优先级翻转问题

1、使用信号量会导致的另一个潜在问题是线程优先级翻转问题。所谓优先级翻转,即当一个高优先级线程试图通过信号量机制访问共享资源时,如果该信号量已被一低优先级线程持有,而这个低优先级线程在运行过程中可能又被其它一些中等优先级的线程抢占,因此造成高优先级线程被许多具有较低优先级的线程阻塞,实时性难以得到保证。如下图所示:有优先级为 A、B 和 C 的三个线程,优先级 A> B > C。线程 A,B 处于挂起状态,等待某一事件触发,线程 C 正在运行,此时线程 C 开始使用某一共享资源 M。在使用过程中,线程 A 等待的事件到来,线程 A 转为就绪态,因为它比线程 C 优先级高,所以立即执行。但是当线程 A 要使用共享资源 M 时,由于其正在被线程 C 使用,因此线程 A 被挂起切换到线程 C 运行

2、如果此时线程 B 等待的事件到来,则线程 B 转为就绪态。由于线程 B 的优先级比线程 C 高,因此线程 B开始运行,直到其运行完毕,线程 C 才开始运行。只有当线程 C 释放共享资源 M 后,线程 A 才得以执行。在这种情况下,优先级发生了翻转:线程 B 先于线程 A 运行。这样便不能保证高优先级线程的响应时间。

优先级反转 (M 为信号量 )
 优先级反转 (M 为信号量 )——来源RT-Thread编程指南

 

参考文献:

1、[野火®]《RT-Thread 内核实现与应用开发实战—基于STM32》

2、《RT-THREAD 编程指南》

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RT-Thread学习笔记——信号量 的相关文章

  • 5.4 Stream Buffer

    Stream Buffer 是一种广义 Cache xff0c 主要功能是避免因为预读而造成的 Cache Pollution 问题 当采用该机制时 xff0c 处理器可以将预读的数据序列放入 Stream Buffer 中而不是放入 Ca
  • VxWorks的信号量机制分析

    VxWorks 的信号量机制分析 VxWorks 信号量是提供任务间通信 同步和互斥的最优选择 xff0c 提供任务间最快速的通信 也是提供任务间同步和互斥的主要手段 VxWorks 提供 3 种信号量来解决不同的问题 二进制信号量 xff
  • Linux系统内存、磁盘占用情况查询

    查看磁盘占用空间 df h 显示所有磁盘的使用情况 xff0c 包括磁盘的总大小 已用空间 可用空间和文件系统类型等 查看运行内存的占用情况 free m 查看进程 1 strong span style color fe2c24 ps s
  • 关于c语言的tcp通讯详细讲解

    目录 1 TCP概览 1 1 TCP基本特征 1 2 TCP通信流程基本原理 2 TCP编程的函数接口说明 3 TCP通讯测试代码 1 TCP概览 TCP全称 Transmition Control Protocol xff0c 即 xff
  • python watchdog:监控文件系统事件的Python库

    python watchdog xff1a 监控文件系统事件的Python库和shell工具 watchdog用来监控指定目录 文件的变化 xff0c 如添加删除文件或目录 修改文件内容 重命名文件或目录等 xff0c 每种变化都会产生一个
  • 关于c语言的udp通讯详细讲解

    目录 1 UDP简介 2 UDP通信流程 3 UDP的函数接口说明 4 UDP通讯测试代码 1 UDP简介 UDP全称 User Datagram Protocol xff0c 即 xff1a 用户数据报协议 是面向无连接的协议 通常 xf
  • python中关于Opencv中关于矩形的函数总结

    最近处理图像 xff0c 以前用的都是matlab xff0c 现在入手python比较慢 xff0c 这几天看到了很多命名和功能相似的函数 xff0c 作个记录总结一下 只是为了能够看下函数知道它是做什么的 xff0c 因此不会对其用法说
  • 在虚拟机中安装Ubuntu-Docker

    在虚拟机中安装Ubuntu Docker 第一步 安装虚拟机 1 安装虚拟机 xff0c 测试网络联网 图 1 安装ubuntu 图 2 设置系统时间 2 建立快照 建立快照 xff1a 快照001 安装成功 第二步 内核更新 可以通过do
  • Java中的toString()方法

    一 toString 方法介绍 toString 方法是 Object 类中的方法 xff0c toString 方法源代码如下 xff1a 1 getClass getName 返回类的全类名 包名 43 类名 2 Integer toH
  • 二十八. Semaphore的使用详解

    前言 Semaphore的官方注释如下 计数信号量 从概念上讲 xff0c 信号量维护一组许可证 xff08 permits xff09 通常 xff0c 每次调用Semaphore acquire方法时如果已经没有许可证 xff0c 则会
  • FreeRTOS vTaskDelay 卡死

    在Task中使用动态方法创建任务后 xff0c 在任务中调用vTaskDelay卡死 xff0c 然后排查问题 xff0c 定时器工作正常 xff0c 如果不使用vTaskDelay也可以一直运行 解决办法 xff0c 改成在main方法中
  • 图像为什么能相减

    前面提到 xff0c 我们可以把图像看作一个函数 xff0c 函数和函数之间是可以相加的 f x y 61 I 1 x y 43 I 2 x y 那么 xff0c 既然函数可以相加 xff0c 函数也是可以相减的 所以 xff0c 图像之间
  • 分析评估和定位声音质量

    64 author wangdaopo 64 email 3168270295 64 qq com 影响音频质量和稳定性的因素 音质好坏的评价 xff0c 响度 音高 音色 xff0c 测试 xff0c 你的语音引擎是基本可用的 xff0c
  • Linux下Clang-format代码格式化

    64 author wangdaopo 64 email 3168270295 64 qq com 1 Clang format 代码格式化介绍 平时团队进行合作的时候需要注意代码的格式 xff0c 虽然很难统一每个人的编码风格 xff0c
  • OOM问题预防和排查内存泄漏及解决方法

    64 author wangdaopo 64 email 3168270295 64 qq com 理解了这个算法我们就理解了为啥 MySQL 躺着也能中枪了 xff0c 因为它的体积总是最大 xff08 一般来说它在系统上占用内存最多 x
  • API调用次数限制实现

    API调用次数限制实现 在开发接口服务器的过程中 xff0c 为了防止客户端对于接口的滥用 xff0c 保护服务器的资源 xff0c 通常来说我们会对于服务器上的各种接口进行调用次数的限制 比如对于某个 用户 xff0c 他在一个时间段 x
  • 虚拟声卡

    一 虚拟声卡是什么 xff1f 虚拟声卡是一种软件产品 xff0c 它只对声音数字信号进行处理 虽然它不能产生声音 xff0c 但可以用来实现声音的传输 存储或混音等功能 1 xff09 虚拟声卡工作原理 xff1a 虚拟声卡通过软件技术实
  • 常用排查调试工具

    64 author wangdaopo 64 email 3168270295 64 qq com 工具 描述 strace Linux分析程序运行速度瓶颈 strace是个功能强大的Linux调试分析诊断工具 尤其是针对源码不可读或源码无
  • meson是用Python语言开发的构建工具,编译需要Ninja(用C++实现)命令。Meson 旨在开发最具可用性和快速的构建系统。

    目录 一 meson和ninja简介 二 meson构建和ninja编译环境搭建 三 meson构建和ninja编译程序使用步骤 四 项目构建工具Meson PyAnnolib pyBit Open Build Service介绍 五 参考
  • 一套实时音视频传输机制和QOS策略选择及语音视频SDK的选型标准

    64 author wangdaopo 64 email 3168270295 64 qq com 语音视频SDK的选型标准 设备的问题 网络部分 物理场景 一套实时的传输机制 sip是通讯协议 xff0c sip只是信令 xff0c 就是

随机推荐

  • sample.html

    目录 一 Html使用注意 1 二 界面设计 xff08 css xff09 9 三 表单使用 15 Jquery判断表单是否修改 15 表单序列化为json对象 16 json对象转为json字符串 16 json对象赋值到表单 xff0
  • Cgi使用

    目录 一 CGI xff08 通用网关接口 xff09 是外部扩展应用程序与 Web 服务器交互的一个标准接口 1 二 web服务器配置 3 三 Cgi编程 xff08 根据CGI标准 xff0c 编写Web服务器运行时的外部扩展应用程序
  • mmap内存映射在应用和内核/驱动交互,进程间交互,大规模数据传输/大文件读写中的使用

    目录 一 Mmap用途 步骤实例 细节 及相关函数 2 1 mmap函数主要用途有三个 xff08 应用和内核 驱动交互 xff0c 进程间交互 xff0c 大规模数据传输 大文件读写 xff09 2 2 使用步骤 xff1a 所有对mma
  • PID控制器整理分享

    概述 日常开发中 xff0c 常常需要对速度 温度等物理量进行稳态控制 xff0c 而在目前的自动化控制原理中 xff0c 使用最为广泛的方法就是PID控制算法 本文简要整理分享PID控制器的使用 正文 PID控制器 xff0c 即比例 积
  • Markdown插入图片的方法

    Markdown插入图片的方法 Markdown插入图片的语法 xff1a alt 属性文本 图片地址 alt 属性文本 图片地址 34 可选标题 34 说明 xff1a 以感叹号开头 接着一个中括号 接着一个小括号 xff0c 里面放上图
  • SLA的基本概念

    SLA的基本概念 现在的产品和系统都非常的复杂 xff0c 彼此连接依赖越来越复杂 xff0c 为了整体的高速运转 xff0c 对每个部件的稳定性越来越高 xff0c 越来越精密 xff0c 发展到一定程度 xff0c 人力已经无法掌控 x
  • ssh远程登录可能出现的问题以及解决办法

    首先 确保server端的ssh服务是开的 xff08 service shhd start xff09 然后在client端输入 xff1a ssh usrname 64 serverip xff08 远程登录 xff09 scp fil
  • 各种滤波原理

    1 维纳滤波 维纳滤波是一种平稳随机过程的最佳滤波理论 xff0c 换句话说就是在滤波过程中系统的状态参数 xff08 或信号的波形参数 xff09 是稳定不变的 它将所有时刻的采样数据用来计算互相关矩阵 xff0c 涉及到解维纳 xff0
  • MTCNN算法提速应用(ARM测试结果评估)

    经博主测试 xff0c mtcnn原三层网络如果用于工程测试 xff0c 误检情况严重 xff0c 在fddb上测试结果也是 xff0c 经常将手或者耳朵 检测为人脸 xff0c 这个很头疼 xff08 因为标注数据 xff01 xff09
  • 国外知名音频库一站式资料和简介

    Speex Speex http blog csdn net xyz lmn article details 8013490 简介 Speex是一套主要针对语音的开源免费 xff0c 无专利保护的音频压缩格式 Speex工程着力于通过提供一
  • vs2010调试工具栏不可用(变灰)解决办法

    菜单中页不能进行调试 使用vs的命令行工具 在命令行中运行 xff1a devenv ResetSkipPkgs 重新打开vs xff08 注 xff1a 第一次没有关闭vs xff0c 运行这条命令后 xff0c 会新打开vs没有变化 x
  • STM32 抗锯齿中文显示另类方案分享

    STM32 抗锯齿中文显示另类方案分享 最近在玩车载液晶仪表 xff0c 之前用Arduino 43 USART液晶 xff0c 后来发现Arduino性能不够 xff0c 就转向STM32 由于需要在液晶屏上显示手机信息 xff0c 先是
  • [Pixhawk] ardupilot源码windows编译教程

    声明 此教程最开始取自于淘宝商家 xff0c 后查找资料发现淘宝商家取自 怒飞垂云 的教程 xff0c 但原教程中有些冗余和错误导致无法通过编译 xff0c 我做更改整理后发出 xff0c 如有疑问请各位一起讨论 目录 一 建立飞控固件编译
  • pixhawk MP地面站 NTF_LED参数相关

    NeoPixel Copter documentation 这一部分里面的NTF相关参数 xff0c 对应MP地面站里的这几项 不是给外接WS2812设置的 我折腾了一下午 xff0c 再更改了其他一些参数后 xff0c 导致飞控上那个最大
  • 无人机betaflight 地面站遥控器通道最低点不是1000,最高点不是2000,中点不是1500的调整办法

    参考老外的视频 How to fix the endpoints of your Rx channels with RXRANGE in Betaflight or Cleanflight EASY FIX 需要进入CLI界面 xff0c
  • 八皇后详解

    历史 八皇后问题是一个古老而著名的问题 xff0c 是回溯算法的典型例题 该问题是十九世纪著名的数学家高斯1850年提出 xff1a 在8X8格的国际象棋上摆放八个皇后 xff0c 使其不能互相攻击 xff0c 即任意两个皇后都不能处于同一
  • KVM虚拟机创建功能详细讲解

    KVM虚拟机创建功能详细讲解 一 KVM虚拟机创建的用户操作 对于用户或者管理员来说 xff0c 虚拟机的创建有着很多的方法 xff0c 例如 xff1a kvm自带命令行工 具 使用virsh命令来创建 使用具有图形界面的virt man
  • JS中堆和栈

    什么是栈和堆 栈和堆的主要作用存储变量 xff0c 根据变量值得不同存储的位置也不同 在JS中 xff0c 栈主要是存储基本类型的变量 xff0c 包括String Number Boolean Undefined Null 和 对象类型的
  • 实例:Python调用c++文件(参数为指针和数组指针)

    本文作为Python调用c 43 43 的进阶实现 xff0c 简单的实现参考文章Python调用c 43 43 高级 xff08 swig xff09 1 一个小小的工作目标 通过Python调用c 43 43 文件 xff0c 生成一个
  • RT-Thread学习笔记——信号量

    前言 本文讲RT Thread的线程间同步之信号量 xff0c 包括为什么要进行线程间同步 信号量创建与删除 信号量获取与释放以及基于STM32的二值信号量示例和计算型信号量示例 xff0c 采用RTT amp 正点原子联合出品潘多拉开发板