uCOS信号量相关函数代码理解

OSSemCreate():创建信号量函数。

{

在定义信号量结构体变量后,调用此函数进行初始化,主要是对信号量结构体的一些赋值。

四个入口参数:指向信号量结构体的指针;信号量名称;资源数目或事件是否发生标志(二值量);返回的错误类型指针。

函数实现过程:① 首先进行安全检查、中断非法调用检查和参数检查,参数检查就检查信号量是否存在,是否被定义了。② 如果检查通过,给结构体元素赋值。标记类型为信号量对象;指定信号量个数;时间戳初始化为0;如果使能了(默认使能)调试代码和变量,指定信号量名字。③ 调用OS_PendListInit()初始化信号量的等待列表。之前我们说过,一般结构体会单独写一个函数来初始化,这里跟消息队列的等待列表一样,都是用的一个结构体,只是组成的链表不同而已。OS_PendListInit()里面就是把等待列表中的指针赋为NULL,个数清0。④ 调用OS_SemDbgListAdd()把信号量添加到信号量调试链表。⑤ 记录信号量个数的全局变量OSSemQty加1,返回无错误。

}

OSSemDel():信号量删除函数  

{

函数思想:信号量的删除函数与消息队列的消息函数一样,因为有任务在等待信号量或者消息队列,所以需要根据选项选择是否有任务正在等待时不删除,然后分类操作:如果选择需要在没有任务等待信号量的条件下删除信号量(OS_OPT_DEL_NO_PEND),则判断目前等待列表中是否有任务,没有则删除,有则返回有任务在等待的错误。若选择不管任务是否在等待信号量都删除(OS_OPT_DEL_ALWAYS),如果等待列表有任务,则要把等待列表中的任务恢复为就绪态,再删除;如果没有,直接删除。

删除不会释放空间:任务的删除和消息队列的删除只是清除了任务控制块,消息队列结构体元素值。信号量的删除和任务的删除,消息队列的删除一样,也只是清除了信号量的元素值,不会释放栈空间

入口参数:信号量指针;选项;返回错误类型。

返回:返回删除信号量前等待其的任务数。

函数实现过程:① 首先进行安全检查,中断非法调用检查,参数检查。这里参数检查除了检查信号量是否为空,还要检查选项是否符合预期(主要是为了用户体验,怕用户写错选项或者选项选择不正确)。删除不像创建信号量,需要检查信号量的类型是否为信号量类型,保证内核对象类型正确。② 进入临界段,获取信号量的等待列表以及等待列表中的任务数。 ③根据选项,如果只在没有任务等待的情况下删除信号量OS_OPT_DEL_NO_PEND,那么就判断任务数是否为0,为0,调用OS_SemDbgListRemove()将信号量从调试列表中移除;使信号量数目减1;调用OS_SemClr()清除信号量内容。③ 如果是OS_OPT_DEL_ALWAYS,进入while循环,调用OS_PendObjDel()一个个把任务从等待列表中移除,然后进行②中任务数为0的操作。④ 返回删除信号量前等待其的任务数。

调用OS_SemClr():删除信号量函数OSSemDel()调用的清除信号量内容的函数OS_SemClr()就是让其中指针元素为NULL,变量只为0,对象类型为none,再调用OS_PendListInit()清除等待列表中的内容。

}

OSSemPost():信号量释放函数

{

如果该信号量用作二值信号量,那么我们在创建信号量的时候其初始值的范围是 0~1,假如初始值为 1 个可用的信号量的话,被获取一次就变得无效了,那就需要我们释放信号量,uCOS 提供了信号量释放函数,每调用一次该函数就释放一个信号量。但是有个问题,能不能一直释放?很显然如果用作二值信号量的话,一直释放信号量就达不到同步或者互斥访问的效果,虽然说 uCOS 的信号量是允许一直释放的,但是,信号量的范围还需我们用户自己根据需求进行决定,保证信号量在使用范围内。当用作二值信号量的时候,必须确保其可用值在 0~1 范围内;而用作计数信号量的话,其范围是由用户根据实际情况来决定的。

3个入口参数:信号量指针;选项;返回错误类型

返回:信号量计数值(就是当前信号量的个数有多少)。

函数过程:① 首先进行安全检查、参数检查和对象类型检查,参数检查包括检查信号量是否创建和选项检查。

符合预期的选项有:OS_OPT_POST_FIFO /* (默认)采用 FIFO 方式发送 */

OS_OPT_POST_LIFO /*采用 LIFO 方式发送消息量*/ (以上两个选项没检查)

OS_OPT_POST_1  /*将消息量发布到最高优先级的等待任务*/

OS_OPT_POST_ALL  /*向所有等待的任务广播消息量*/

OS_OPT_POST_NO_SCHED /*发送消息量但是不进行任务调度*/

② 调用OS_TS_GET()获取当前时间戳,作为发送信号量的时间戳。③ 如果是在中断中发送信号量,调用OS_IntQPost();如果是在任务中发送,调用函数OS_SemPost()。

-调用OS_SemPost()

4个入口函数:信号量指针;选项;时间戳;返回的错误类型

返回:信号量个数

函数过程:① 关中断,加载该信号量的等待列表,就是把信号量的等待列表指针赋值给一个变量,方便操作。这里为什么要进入临界段?因为定义的信号量结构体变量是全局的,创建信号量函数只是初始化里面的元素,访问公共资源,需要关中断防止出错。② 如果没有任务在等待信号量,先用switch-case多分支选择语句判断是否释放的信号量会导致信号量计数值溢出,因为之前说过uCOS提供的函数允许一直释放信号量不出错,所以这里要判断如果计数值溢出,则返回。这就是uCOS提供的办法:允许你一直调用释放信号量函数,函数里面我会判断,溢出就返回就行了。这里通过信号量类型字节数(sizeof)进行最大信号量个数判断,有8字节,16字节,32字节。溢出的信号量个数有:2^8-1,2^16-1,2^32-1。但是这里判断信号量溢出的上限是数据类型能够容纳的个数,而不是用户自定义的最大值。比如,8字节,它判断是否到达了256,但是用户实际创建的可能就说目前可用的信号量为6等等。③ 如果没有任务在等待信号量且信号量不会溢出,那么就让信号量数目加1,更新信号量的时间戳。注意:信号量不是消息队列消息队列在没有任务等待消息时,需要把任务发送的消息送进消息列表,信号量就是一个数字,只是一个标志,不传递其他任何数据消息;如表示事件还有多少没被处理;信号量有阻塞机制,任务在等待信号量时进入阻塞,那么CPU不会调度这个任务,拥有更高效率这是信号量与全局变量标志开关中断的区别)。④ 若有任务在等待信号量,就把信号量给等待的任务;若是广播形式,则等待列表中的任务都会获得该信号量,信号量个数不变;并把任务从等待列表(阻塞态)中移除。因为要操作任务与信号量的列表,系统不希望其他任务干扰,所以关闭调度器。根据选项,如果要把信号量发布给所有等待的任务,获取任务数;否则任务数为1。然后根据这个任务数进行循环,调用OS_Post()依次把任务从时基列表和等待列表中移除,并插入到就绪列表中。这里需要注意的是,信号量中调用OS_Post()仅仅是把任务从阻塞态恢复,并没有像消息队列那样,把消息赋给任务那怎么判断任务获取了信号量因为信号量只是个标志,所以不会把信号量个数赋给任务,当任务从阻塞态恢复时,就认为任务获得了信号量。⑤ 如果 opt 没选择“发布时不调度任务”,那么就进行任务调度。

}

OSSemPend():信号量获取函数

{

信号量获取机制:与释放信号量对应的是获取信号量,我们知道,当信号量有效的时候,任务才能获取信号量,当任务获取了某个信号量的时候,该信号量的可用个数就减一,当它减到 0 的时候,任务就无法再获取了,并且获取的任务会进入阻塞态(等待列表),等待有任务释放信号量。 uCOS 支持系统中多个任务获取同一个信号量,假如信号量中已有多个任务在等待,那么这些任务会按照优先级顺序进行排列,如果信号量在释放的时候选择只释放给一个任务,那么在所有等待任务中最高优先级的任务优先获得信号量,而如果信号量在释放的时候选择释放给所有任务,则所有等待的任务都会获取到信号量。

5个入口参数:信号量指针;等待超时时间;选项;信号量发布、挂起中止或删除的时间戳;返回错误类型。(注意这里的时间戳是信号量发布时的时间戳,并不是等待时的时间戳也不是等到信号量时的时间戳。而且允许用户传递时间戳空指针,意味着用户不需要返回时间戳

返回:信号量个数(资源数)

函数过程:① 进行安全检查,中断中非法调用检查,参数检查(信号量是否为空,选项检查),对象类型检查。信号量获取的选项包括OS_OPT_PEND_BLOCKING(等待不到对象进行阻塞)、OS_OPT_PEND_NON_BLOCKING(等待不到对象不进行阻塞)。② 获取信号量,首先判断信号量资源可不可用,就是大于0不。如果信号量可用,任务不用进入等待列表,直接资源数减1,返回无错误和信号量当前资源数。③ 如果没有资源可用,应该将任务插入等待列表,如果选项选择了不阻塞任务,返回错误类型为“等待渴求阻塞”(可以选项选择不等待,判断该错误返回,以此判断信号量是否用完。)。 ④ 如果没有资源可用,选择了阻塞任务,先判断调度器有无被锁,被锁就返回错误类型为“调度器被锁”,如果没有就调用OS_Pend()把任务插入等待列表和时基列表(有延时的话)。⑤ 调用OSSched(),进行任务切换,因为该任务等待阻塞了,相当于延时阻塞,为了高效率利用CPU,在等待消息的这段时间里,应该把控制权交给其他任务。⑥ 程序运行到这里,应该是任务获取到信号量,或者超时,根据任务目前的状态分类处理。最后获取信号量当前资源数目并返回。

}

}

おすすめ

転載: blog.csdn.net/m0_43443861/article/details/125960773