在UCOSIII中有可能会有多个任务会访问共享资源,因此信号量最早用来控制任务存取共享资源,现在信号量也被用来实现任务间的同步以及任务和ISR间同步。在可剥夺的内核中,当任务独占式使用共享资源的时候,会出现低优先级的任务先于高优先级任务运行的现象,这个现象被称为优先级反转,为了解决优先级反转这个问题,UCOSIII引入了互斥信号量这个概念。本章,就来讲解一下UCOSIII的信号量和互斥信号量。
信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放才能继续执行。
信号量分为2种:二进制信号量与计数型信号量,二进制信号量只能取0和1两个值,计数型信号量不止可以取2个值,在共享资源中只有任务可以使用信号量,中断服务程序则不能使用。
1.1 二进制信号量
某一资源对应的信号量为1的时候,那么就可以使用这一资源,如果对应资源的信号量为0,那么等待该信号量的任务就会被放进等待信号量的任务表中。在等待信号量的时候也可以设置超时,如果超过设定的时间任务没有等到信号量的话那么该任务就会进入就绪态。任务以“发信号”的方式操作信号量。可以看出如果一个信号量为二进制信号量的话,一次只能一个任务使用共享资源。
1.2 计数型信号量
有时候我们需要可以同时有多个任务访问共享资源,这个时候二进制信号量就不能使用了,计数型信号量就是用来解决这个问题的。比如某一个信号量初始化值为10,那么只有前10个请求该信号量的任务可以使用共享资源,以后的任务需要等待前10个任务释放掉信号量。每当有任务请求信号量的时候,信号量的值就会减1,直到减到为0.当有任务释放掉信号量的时候信号量的值就会加1.
1.3 创建信号量
要想使用信号量,肯定需要先创建一个信号量,我们使用函数OSSemCreate()来创建信号量,函数原型如下:
1.4 请求信号量
当一个任务需要独占式的访问某个特定的系统资源时,需要与其他任务或中断服务程序同步,或者需要等待某个事件的发生,应该调用函数OSSemPend(),函数原型如下:
1.5 发送信号量
任务获得信号量以后就可以访问共享资源了,在任务访问完共享资源以后必须释放信号量,释放信号量也叫发送信号量,使用函数OSSemPost()发送信号量。如果没有任务在等待该信号量的话则OSSemPost()函数只是简单的将信号量加1,然后返回到调用该函数的任务中继续运行。如果有一个或多个任务在等待这个信号量,则优先级最高的任务获得这个信号量,然后由调度器来判定刚获得信号量的任务是否为系统中优先级最高的就绪任务,如果是,则系统将进行任务切换,运行这个就绪任务,OSSemPost()函数原型如下:
我们前面提过信号量主要用于访问共享资源和进行任务同步,这里我们先做一个直接访问共享资源的实验,看看会带来什么后果。
从图中可以看出,系统并没有按照我们想要的方式输出信息,我们想要的输出像下面一样。
我们分析一下源码,在任务1向share_resource拷贝数据“first task running!”以后就因为delay_ms()函数系统进行了任务切换。任务2开始运行,这时任务2又向share_resource拷贝了数据“second task running!”,任务2也因为delay_ms()函数发生任务切换,任务1接着运行,但是这时share_resource已经被修改为“second task running!”,因此输出就会和我们预计的不一样了,从而导致错误的发生,这个就是多任务共享资源区带来的问题!所以在任务访问共享资源区的时候我们要对其进行保护。下面我们展示一下使用信号量来保护共享资源区。
在上例中我们对于share_resource的访问并没有进行保护,从而导致了错误的发送,这一节我们使用信号量来进行共享资源区的访问。
从上图中可以看出,串口按照我们设定的来输出信息,共享资源区并没有被其他任务随意修改。
信号量现在更多的被用来实现任务的同步以及任务和ISR间的同步,信号量用于任务同步如下图。
上图中用一个小旗子代表信号量,小旗子旁边的数值N为信号量计数值,表示发布信号量的次数累计值,ISR可以多次发布信号量,发布的次数会记录为N。一般情况下,N的初始值是0,表示事件还没有发生过。在初始化时,也可以将N的初值设为大于0的某个值,来表示初始情况下有多少信号量可用。
等待信号量的任务旁边的小沙漏表示等待任务可以设定超时时间。超时的意思是该任务只会等待一定时间的信号量,如果在这段时间内没有等到信号量,UCOSIII就会将任务置于就绪表中,并返回错误码。
优先级反转在可剥夺内核中是非常常见的,在实时系统中不允许出现这种现象,这样会破坏任务的预期顺序,可能会导致严重的后果,下图就是一个优先级反转的例子。
在这种情况下,任务H的优先级实际上降到了任务L的优先级水平。因为任务H要一直等待直到任务L释放其占用的那个共享资源。由于任务M剥夺了任务L的CPU使用权,使得任务H的情况更加恶化,这样就相当于任务M的优先级高于任务H,导致优先级反转。
从上例中可以看出,当一个低优先级任务和一个高优先级任务同时使用同一个信号量,而系统中还有其他中等优先级任务时。如果低优先级任务获得了信号量,那么高优先级的任务就会处于等待状态,但是,中等优先级的任务可以打断低优先级任务而先于高优先级任务运行(此时高优先级的任务在等待信号量,所以不能运行),这就是出现了优先级反转的现象。
为了避免优先级反转这个问题,UCOSIII支持一种特殊的二进制信号量:互斥信号量,用它可以解决优先级反转问题,如下图。
注意:只有任务才能使用互斥信号量(中断服务程序则不可以),UCOSIII运行用户嵌套使用互斥型信号量,一旦一个任务获得了一个互斥型信号量,则该任务最多可以对该互斥型信号量嵌套使用250次,当然该任务只有释放相同的次数才能真正释放这个互斥信号量。
与普通信号量一样,对于互斥信号量也可以进行许多操作,如下图,文件os_mutex.c是关于互斥信号量的。
7.1 创建互斥型信号量
创建互斥信号量使用函数OSMutexCreate()函数,函数原型如下:
7.2 请求互斥型信号量
当一个任务需要对资源进行独占式访问的时候就可以使用函数OSMutexPend(),如果该互斥信号量正在被其他的任务使用,那么UCOSIII就会将请求这个互斥信号量的任务放置在这个互斥信号量的等待表中。任务会一直等待,直到这个互斥信号量被释放掉,或者设定的超时时间到达为止。如果在设定的超时时间到达之前信号量被释放,UCOSIII将会恢复所有等待这个信号量的任务中优先级最高的任务。
注意!如果占用互斥信号量的任务比当前申请该互斥信号量的任务优先级低的话, OSMutexPend()函数会将占用该互斥信号量的任务的优先级提升到和当前申请任务的优先级一样。当占用该互斥信号量的任务释放掉该互斥信号量以后,恢复到之前的优先级。OSMutexPend()函数原型如下:
7.3 发送互斥信号量
我们可以通过调用函数OSMutexPost()函数来释放互斥型信号量,只有之前调用过函数OSMutexPend()获取互斥信号量,才需要调用OSMutexPost()函数来释放这个互斥信号量,函数原型如下:
从上面的分析可以看出互斥信号量有效的抑制了优先级反转现象的发生。
前面我们使用信号量都需要先创建一个信号量,不过在UCOSIII中每个任务都有自己的内嵌的信号量,这种功能不仅能够简化代码,而且比使用独立的信号量更有效。任务信号量是直接内嵌在UCOSIII中的,任务信号量相关代码在os_task.c中。任务内嵌信号量相关函数如下图所示:
9.1 等待任务信号量
等待任务内嵌信号量使用函数OSTaskSemPend(),OSTaskSemPend()允许一个任务等待由其他任务或者ISR直接发送的信号,使用过程基本和独立的信号量相同,OSTaskSemPend()函数原型如下:
9.2 发布任务信号量
OSTaskSemPost()可以通过一个任务的内置信号量向某个任务发送一个信号量,函数原型如下:
本文发布于:2024-01-31 22:14:55,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170671049531738.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |