ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

FreeRTOS (了解同步互斥的缺陷)

FreeRTOS (了解同步互斥的缺陷)

同步(Synchronization,信号量)
含义:一个任务等待某个事件发生;事件来了,任务才继续执行,任务之间配合完成工作。
生活类比:等快递。快递员(任务 A)送完快递(事件发生),你(任务 B)收到通知,才开门取件。
典型场景:
按键检测任务检测到按键按下,通知业务任务处理;串口收到数据,通知解析任务处理。

互斥(Mutual Exclusion,互斥锁 mutex)
含义:同一个共享资源,同一时刻只允许一个任务使用,任务之间互相排斥。
目的:防止多个任务同时访问同一个资源造成错乱。
生活类比:公共厕所。同一时间只能一个人进去,其他人排队等待。
典型场景:
多个任务操作同一个 LCD、串口、全局变量。

同步的缺陷:

同步代码示例:

staticintg_LCDCanUse=1;//判断任务一是否执行完,执行完置1,执行任务二,volatile是告诉CPU这个值是随时变化的,要到内存去取值staticvolatileintCalcTask_Flag=0;staticuint64_tg_Time;staticuint32_tg_Sum;staticuint32_ti;voidCalcTask(void*parms){g_Time=system_get_ns();for(i=0;i<10000000;i++){g_Sum+=i;}g_Time=system_get_ns()-g_Time;//计算时间CalcTask_Flag=1;vTaskDelete(NULL);}voidLcdPrintTask(void*parms){intlen;while(1){LCD_PrintString(0,0,"Waiting Time");while(CalcTask_Flag==0);if(g_LCDCanUse==1){g_LCDCanUse=0;LCD_ClearLine(0,0);LCD_ClearLine(0,2);len=LCD_PrintString(0,0,"sum:");//赋值字符串长度给lenLCD_PrintHex(len,0,g_Sum,0);//pre=0: 不带0x前缀LCD_ClearLine(0,2);len=LCD_PrintString(0,2,"Time:");//赋值字符串长度给lenLCD_PrintSignedVal(len,2,g_Time/1000000);//显示时间g_LCDCanUse=1;}vTaskDelete(NULL);}}

现象:

在原代码基础上加了这个,让任务二在任务一执行的时候进入阻塞状态,不参与任务调度

现象变成这样:

可以看到时间减少了一半,这说明任务二在等待任务一执行的时候会浪费很多的CPU资源,所以我们使用同步的需思考怎么提高处理器的性能,让那些等待的任务进入阻塞,不参与CPU的调度

互斥的缺陷:

从这个图我们可以看到,如果在红框内执行代码,假设任务A发出一个start信号,然后任务调度器切到了任务B执行,这时任务B又发出一个start信号和发送地址,这个I2C时序就被打乱了,所以使用红框代码来操作LCD时,要互斥使用,必须等任务A执行完才能轮到你任务B执行,这时我们使用了全局变量来保护临界资源,但这能万无一失吗?往下看


可以看到,假设任务A执行到了108行的时候,切换到了任务B执行,这时因为任务A还没将变量设置为0,所以任务B还是能进入这个if语句的,但任务B执行红框内代码执行到一半时,又切换到任务A,因为任务A是在108行被切换的,所以它也能进入if语句里面,将变量再次置0,也同样能操控I2C,那这时I2C时序又被打乱了,虽然这个概率很小,但不是不可能。

再举一个例子:

从这个图中,我们假设有任务A和任务B来执行这个函数,在汇编语言中,bCanUse–代码可以拆分为三条汇编指令,假设任务A刚刚将bCanUse变量的值写入到R0寄存器时就切换到任务B执行,那么因为bCanUse没有被改变,所以任务B还是能继续执行,当任务B执行if语句执行到一半时,因为任务A已经将bCanUse变量的值写入到R0寄存器了,所以它也能正常执行任务,这时I2C时序又被打断了,怎么办呢?

既然是在设置变量这里因为切换任务出现了问题,那我们关中断不就好了吗?如下:
示例一:

示例二:

可以看到我们在判断修改这个变量之前就把中断给关掉,等我们修改完变量,在重新开启中断,这样不就行了吗?

不过这里依旧有不足:
以示例一为例子:


可以看到假设当A在执行任务时,因为变量的改变,任务B无法进入到while循环里面,但任务B还是不断执行申请进入到while循环这些没有意义的事情,那这不就跟同步一样占用了CPU资源吗,所以我们要在任务A还没有执行完时,让任务B进入阻塞状态,不参与任务的调度,当任务A执行完后会把任务B唤醒,让任务B去执行任务,这样就能提高CPU运行效率

任务之间的通信:
两个任务之间怎么通信,上面同步的例子已经告诉我们:全局变量(任务A把计算结果放到全局变量里,然后任务B读取这个全局变量,这就实现了通信的目的)

那如果我任务A写这个变量写到一半的时候,任务B就来读取这个变量,那任务B读取的就是一个错误的值,这是有可能的,所以怎么通信不重要,重要的是怎么保证通信的结果是正确的

所以我们引用了互斥的方法,在互斥的情况下通信,除了互斥我们还要提高效率

FreeRTOS就提供了很多方法,首先通过互斥的方法来保证通信的正确性,又通过阻塞和唤醒的同步机制来提高效率,下面是提供的多种方法

返回列表