此笔记由个人整理
华为IOT LiteOS开发实战营
第三天
一、简介
- 信号量(Semaphore)是一种实现任务间通信的机制,实现任务之间同步或临界资源的互斥访问。常用于协助一组相互竞争的任务来访问临界资源。
- 在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
- 通常一个信号量的计数值用于对应有效的资源数,表示剩下的可被占用的互斥资源数。其值的含义分两种情况:
- 0,表示没有积累下来的Post操作,且有可能有在此信号量上阻塞的任务。
- 正值,表示有一个或多个Post下来的释放操作。
- 4、以同步为目的的信号量和以互斥为目的的信号量在使用有如下不同:
- 用作互斥时,信号量创建后记数是满的,在需要使用临界资源时,先取信号量,使其变空,这样其他任务需要使用临界资源时就会因为无法取到信号量而阻塞,从而保证了临界资源的安全。
- 用作同步时,信号量在创建后被置为空,任务1取信号量而阻塞,任务2在某种条件发生后,释放信号量,于是任务1得以进入READY或RUNNING态,从而达到了两个任务间的同步。
二、运作原理
- 信号量初始化:为配置的N个信号量申请内存(N值可以由用户自行配置,受内存限制),并把所有的信号量初始化成未使用,并加入到未使用链表中供系统使用。
- 信号量创建:从未使用的信号量链表中获取一个信号量资源,并设定初值。
- 信号量申请:若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。
- 信号量释放:若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。
- 信号量删除:将正在使用的信号量置为未使用信号量,并挂回到未使用链表。
- 信号量允许多个任务在同一时刻访问同一资源:但会限制同一时刻访问此资源的最大任务数目。访问同一资源的任务数达到该资源的最大数量时,会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。
- 公共资源有四个任务数,信号量都分别被线程1、2、3、4获取后,此时此资源就会锁定而不让线程5进入,线程5及后面的线程都进入阻塞模式,当线程1工作完成而释放出信号量,线程5立即获得信号而得到执行。如此往复。
三、使用场景及开发流程
使用场景
- **互斥锁:**用作互斥时,信号量创建后记数是满的,在需要使用临界资源时,先申请信号量,使其变空,这样其他任务需要使用临界资源时就会因为无法申请到信号量而阻塞,从而保证了临界资源的安全。
- **任务间同步:**用作同步时,信号量在创建后被置为空,任务1申请信号量而阻塞,任务2在某种条件发生后,释放信号量,于是任务1得以进入READY或RUNNING态,从而达到了两个任务间的同步。
- **资源计数:**用作资源计数时,信号量的作用是一个特殊的计数器,可以递增或者递减,但是值永远不能为负值,典型的应用场景是生产者与消费者的场景。
- **中断与任务的同步:**用作中断与任务的同步时,可以在中断未触发时将信号量的值置为0,从而堵塞断服务处理任务,一旦中断被触发,则唤醒堵塞的中断服务处理任务进行中断处理。
开发流程
- 创建信号量->申请信号量->释放信息量->删除信息量
1)如果有任务阻塞于指定信号量,则唤醒该信号量阻塞队列上的第一个任务。该任务进入就绪态,并进行调度
2)如果没有任务阻塞于指定信号量,释放信号量成功
四、操作系统抽象层
- 信息量创建
osal_semp_create(osal_semp_t *semp,int limit,int initvalue);
- 信息量删除
osal_semp_del(osal_semp_t semp);
- 信息量申请
osal_semp_pend(osal_semp_tsemp,int timeout);
- 信息量释放
osal_semp_post(osal_semp_t semp);
五、代码实践
- 创建任务,选择信息量案例
基本实验
#include <osal.h>
#define USER_TASK1_PRI 12//低优先级
#define USER_TASK2_PRI 11//高优先级
osal_semp_t sync_semp;//定义信息量
static int user_task1_entry()//任务1
{
while(1)
{
printf("task 1 post a semp!\r\n");
osal_semp_post(sync_semp);//释放信号量
osal_task_sleep(2*1000);
}
}
static int user_task2_entry()//任务2
{
while (1)
{
printf("task2 is waiting for a semp...\r\n");
osal_semp_pend(sync_semp, cn_osal_timeout_forever);//申请信息量
printf("task 2 access a semp!\r\n");
}
}
int standard_app_demo_main()
{
osal_semp_create(&sync_semp, 1 ,0 );//创建信号量
printf("sync_semp semp create success.\r\n");
osal_task_create("user_task1",user_task1_entry,NULL,0x400,NULL,USER_TASK1_PRI);
osal_task_create("user_task2",user_task2_entry,NULL,0x400,NULL,USER_TASK2_PRI);
return 0;
}
- 结果:通过信号量完成数据的同步
扩展实验
- 通过返回值判断函数的运行状态
- 删除创建的信号量;
#include <osal.h>
#define USER_TASK1_PRI 12//低优先级
#define USER_TASK2_PRI 11//高优先级
osal_semp_t sync_semp;//定义信号量
static int user_task1_entry()
{
int ret;
int n = 0;
while (1)
{
n++;
ret = osal_semp_post(sync_semp);
if (true == ret)
{
printf("task 1 post a semp OK\r\n");
}
else
{
printf("task 1 post a semp ERROR\r\n");
return 0;
}
if (n == 3)
{
ret = osal_semp_del(sync_semp);
if (true == ret)
{
printf("task 1 delete a semp OK\r\n");
}
else
{
printf("task 1 delete a semp ERROR\r\n");
return 0;
}
}
osal_task_sleep(4 * 1000);
}
}
static int user_task2_entry()
{
int ret;
while (1)
{
printf("task2 is waiting for a semp...\r\n");
ret = osal_semp_pend(sync_semp, cn_osal_timeout_forever);//申请信号量
if (true == ret)
{
printf("task 2 pend a semp OK!\r\n");
}
else
{
printf("task 2 pend a semp ERROR!\r\n");
return 0;
}
}
}
int standard_app_demo_main()
{
osal_semp_create(&sync_semp, 1, 0);
printf("sync_semp semp create success.\r\n");
osal_task_create("user_task1", user_task1_entry, NULL, 0x400, NULL, USER_TASK1_PRI);
osal_task_create("user_task2", user_task2_entry, NULL, 0x400, NULL, USER_TASK2_PRI);
return 0;
}
- 结果