技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152
这个信号量就类似于锁,给代码,或共享资源,比如输入输出寄存器等加锁,保证同一个时间
只能有一个函数去操作对应的值,然后现在基本用来做任务同步
是什么意思呢?
比如数据的接收就需要同步,因为不同步有可能数据就会乱
比如这里,通过中断进行数据接收,放到ISR寄存器,然后有个任务task1,去发送数据,那么
这里的这个中断和这个task1就需要同步,才能保证数据接收正确.
然后再比如:
key按键是一个任务,然后led亮是一个任务,
那么key按键和led亮需要保持同步也就是,按下key,led就要亮,要保证这个顺序.
这里再说一下这个信号量,
这个信号量可以理解为就是一个变量,
比如这里有3个任务,A,C,D,那么
如果有个变量作为信号量,比如变量b,只有当变量b是大于0的时候,其他的A,C,D任务才能
进行使用资源,当b减少到0的时候,那么CD就只能等待,当再变量b再恢复到
0以上的时候,CD就又可以使用这个资源.
那么pend是等待信号量可用,或者是请求信号量,post是发送信号量,或者叫释放信号量.
比如这个时候有个信号量是5,那么如果一直pend,一直请求的话,如果这个信号量减少到0,那么
C,D就没办法使用了.就会停止运行.
去看看代码
看看这个信号量的种类,一种是二进制的,也就是只能一个任务使用的资源,比如
打印机,还有是计数形的,某个资源可同时被几个任务使用,比如缓存池,有10个缓存块,
那么同时最多可支持10个任务来使用内存池,就可以计数到10,把这个信号量设置为10就可以了.,
去看看代码:
首先
这OSSemCreate这个是,创建一个信号量.
要创建一个信号量,首先要定义一个信号量.
然后看一下,这里OS_SEM是信号量,
CPU_CHAR P_PNAME这是信号量的名字
cnt是信号量,可以计数,这个计数的初始值
p_err这个是信号量的错误代码
去看看这个信号量.
这里,主要看这个
OS_SEM_CTR Ctr这个值,这个就是,信号量的值,大于0的时候资源可以用,为0就要等待.
这个值可以读取,一会去看看
这个OSSemDel是删除信号量,
第一个p_sem是信号量,第二个是选项opt,第三个是错误码
选项都是有什么呢
可以看到这里:
OS_OPT_DEL_NO_PEND这个意思是,如果有很多任务还在使用这个信号量,就等待任务使用信号量结束以后
再去删除这个信号量
然后
OS_OPT_DEL_ALWAYS这个意思是不管还有没有其他任务在使用这个信号量,直接删除.
再看看这个
OSSemPend这个()函数是等待信号量函数.
第一个参数是要等待的信号量,
第二个参数是timeout,超时
可以看到这里是时钟tick,时间片,如果5ms是一个时钟周期,那么设置为2,就是10ms
如果这里设置了,比如2,就等待10ms,就超时了,超时以后就可以去执行其他任务去了.
如果这里timeout设置成了0,那么意思就是,一直去请求信号量,也就是一直去等待信号量了.
一直等待,就会造成任务不继续执行了,所以这里timeout这个要注意.
第三个参数是opt
OS_OPT_PEND_BLOCKING这个的意思就是,如果请求信号量,
这里请求不到,就会程序阻塞在这里.
OS_OPT_PEND_NON_BLOCKING这个是,如果请求不到信号量就直接返回了不等了.
这里在看看p_ts
这个是个时间戳,就是返回一个请求到信号量的时刻.
最后一个参数是请求的错误码
这个OSSemPendAbort这个是,取消等待
第一个参数是操作的哪个信号量
第二个参数是,选项.
这个OS_OPT_PEND_ABORT_L这个是仅仅,取消优先级最高的任务的等待.
OS_OPT_PEND_ABORT_ALL这个是,取消所有的等待任务
OS_OPT_POST_NO_SCHED这个是,禁止在本函数内进行任务调度操作.
第3个参数就是错误码
OSSemPost()这个是发送信号量的函数,
这个第一个参数是发送哪个信号量.
第二个是OS_OPT是一个选项去看看
这里OS_OPT_POST_1这个是,像正在等待信号量的所有任务中,优先级最高是任务去发送信号量
OS_OPT_POST_ALL这个是,向所有的任务去发送信号量.
OS_OPT_POST_NO_SCHED这个是禁止本函数内进行任务调度.
接下来
看个案例,如果咱们不使用信号量,而直接去操作访问共享资源会怎么样
首先创建两个任务
然后定义一个数组作为共享资源区.
去看看任务1,这里咱们会向共享资源区也就是上面
声明的share_resource去copy内容到这个共享资源区,
然后再延时200ms,这个延时的时候要注意,延时的过程中是会发生任务调度的.
printf()
一直到这个时候,其实这个task1都是在占用着这个share_resource这个共享资源的.
再去看看task2
可以看到task2也是这样做的,
那么按照咱们看的结果应该是,
task1打印一个字符串,然后
task2打印一个字符串,然后
task1打印一个字符串....是这样.
但是,由于现在我们没有对共享资源区做代码保护,他是什么效果呢,咱们看看
下载到开发版
可以看到打印出来的效果全部是任务2的,任务1都没有抢占到资源.
这个打印是乱套的,
这个就是没有用信号量保护的现象.
然后咱们再看看,使用信号量来访问,共享资源区.
也是两个任务,跟上面是一样的,然后
声明
共享资源区
share_resource[30]
然后这里要用信号量,先声明一个信号量OS_SEM MY_SEM
在开始任务重首先去创建信号量,这里定义的信号量是1,也就是,只能用一次,同时,这个信号量
OS_SEM_CTR 是1
然后task1中
使用的时候可以看去请求
OSSemPend,等待信号量,请求信号量,这里超时时间是0,也就是一直等待,
然后,OS_OPT_PEND_BLOCKING这个是等待不到信号量,系统就在这不继续执行,
然后0,这个是需要返回时间戳,也就是请求到信号量的时间点.
使用完信号量以后,再释放信号量,发送信号量.
可以看到任务2也是这样,使用信号量来保护共享资源区
下载到开发版,再去看看
可以看到这样就得到自己想要的结果了,
当然你也可以通过禁止任务调度,或者关闭中断的方式实现也行.
一般建议不要关闭中断的方式,采用信号量,或者是,上锁,任务调度器加锁的机制都可以
中断的方式会影响系统的稳定性会关闭滴答定时器等,串口的通信等可能会有影响.