目录
1.概念
1.1 二值型信号量
1.2 计数型信号量
2.信号量的创建(API)
2.1动态信号量创建
2.2动态信号量的删除
2.3静态信号量的创建
2.4静态信号量的删除
3.信号量的获取
4.信号量的释放
5.实际使用
嵌入式实时性操作系统中,运行的主要为线程和ISR(中断服务程序),运行步骤有时需要同步处理(相互之间交换数据),有时需要互斥处理(同一时刻只有一个线程访问公共资源),为满足需求,操作系统提供了统称为线程间通讯的功能。
线程间通讯包括:信号量,互斥量,事件,邮箱,消息队列。本文讲述信号量的基本概念以及其具体的代码使用。
线程通过信号量来的控制来访问公共资源,信号量的值就是未被线程使用的公共资源的个数,每个信号量对象都有一个对应的信号量值和线程等待队列,线程等待队列为未成功获取信号量的线程。
信号量分为计数信号量和二值信号量。
二值信号量只有0和1,类似于一个标志位,相比于使用一个全局变量作为标志位,二值信号量可以更好的利用CPU资源,避免浪费,当线程信号量为0时,申请信号量线程进入阻塞状态,信号量为1时,唤醒该线程。
计数型型号了数值范围为0-65535,可以允许多个线程访问公共资源,但会限制访问资源的最大数目,当达到最大数目,后续申请资源的线程会进入阻塞状态,直到有线程释放了信号量。
以上两种会经常用到计数型信号量。
信号量创建分为动态信号量和静态信号量,类似与线程创建,区别为是否由内核分配空间。
动态信号量创建API,
static rt_sem_t dynamic_sem = RT_NULL;
void main(){
dynamic_sem= rt_sem_create ( const char * name,
rt_uint32_t value,
rt_uint8_t flag
)
}
首先创建一个指向信号量的指针dynamic_sem,然后将信号量创建函数返回参数赋予dynamic_sem,信号量创建函数rt_sem_create参数为信号量名称,信号量数值,以及信号量标志位,创建错误时返回RT_NULL。
信号量标志位决定了线程等待队列排队的方式,标志位有两个值
动态信号量删除API
static rt_sem_t dynamic_sem = RT_NULL;
dynamic_sem= rt_sem_create ( const char * name,
rt_uint32_t value,
rt_uint8_t flag
)
rt_sem_delete (dynamic_sem) ; //删除信号量dynamic_sem
参数为信号量 对象句柄,即创建的指向信号量的指针。本文中为dynamic_sem
静态信号量创建API
静态信号量的创建需要先定义一个结构体,类型为struct rt_semaphore
rt_err_t rt_sem_init ( rt_sem_t sem,
const char * name,
rt_uint32_t value,
rt_uint8_t flag
)
参数中sem为指向刚刚创建的类型为struct rt_semaphore的结构体的指针,其他和动态创建信号量相同。
静态信号量的删除API
rt_sem_detach ( rt_sem_t sem )
删除函数参数sem为信号量句柄,即指向信号量的指针,本文中为dynamic_sem
信号量获取API
static rt_err_t result ;
result=rt_sem_take (rt_sem_t sem, rt_int32_t time);
首先创建结构体变量result,将信号量获取函数返回值赋予result,若result为RT-EOK,则成功获取信号量。
参数说明:
线程通过调用该函数获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量, 并且相应的信号量值会减 1;如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请 该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。
解释:sem为信号量对应指针,本文中为dynamic_sem,参数time为等待时间,根据设定不同的等待时间,线程会执行三种不同的操作:
无等待信号量获取API
rt_sem_trytake ( rt_sem_t sem )
参数sem为信号量指针句柄
当使用此函数时,线程不再等待,如果线程无法获取信号量,则直接返回错误码,反之则获取成功。
信号量释放API
rt_sem_release ( rt_sem_t sem )
该函数将释放一个信号量,当信号量的值等于零时,并且有线程等待这个信号量时, 释放信号量将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量;否则 将把信号量的值加一。
#include
static rt_sem_t dynamic_sem = RT_NULL; //定义线程指针
ALIGN(RT_ALIGN_SIZE) //指针格式对齐
void count(void *parameter)
{
static int value=0;
while(value<10){
value++;
rt_thread_mdelay(1500); //每隔1.5s释放一次信号量
rt_kprintf("count release xinhao\n");
rt_sem_release(dynamic_sem); //信号量释放
}
}
ALIGN(RT_ALIGN_SIZE)
void result(void *parameter)
{
static rt_err_t getxinhao ; //信号量获取返回值
static int number=0;
while(1){ //循环打印
getxinhao=rt_sem_take (dynamic_sem, RT_WAITING_FOREVER); //信号量获取
if (getxinhao==RT_EOK) { //获取到信号量则加1
number++;
rt_kprintf("number=%d\n",number);
}
else { //获取信号量失败则打印失败
rt_kprintf("xinhao get failed");
rt_sem_delete(dynamic_sem);
return;
}
}
}
int main(){
dynamic_sem= rt_sem_create ("xinhao1",0,RT_IPC_FLAG_FIFO ) ; //创建信号量xinhao1,最大访问数目1,线程等待队列为先进先出模式
if (dynamic_sem!= RT_NULL)
{
rt_kprintf("xinhao create done"); //信号量创建成功标志
}
else {
rt_kprintf("xinhao create failed");
}
static rt_thread_t count2 = RT_NULL ;
count2= rt_thread_create("count", //线程名称
count, //线程入口函数
RT_NULL, //参数
1024, //线程栈
25, //优先级
5); //时间片
if (count2 != RT_NULL)
{
rt_thread_startup(count2); //线程创建成功则开启线程
}
else{
rt_kprintf ("count failed\n") ;
}
static rt_thread_t result2 = RT_NULL ;
result2= rt_thread_create("result", //线程名称
result, //线程入口函数
RT_NULL, //参数`
1024, //线程栈
24, //优先级
5); //时间片
if (result2 != RT_NULL)
{
rt_thread_startup(result2); //线程创建成功则开启线程
}
else{
rt_kprintf ("result failed\n") ;
}
return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(main, task1);
参考官方示例编写,官方为创建两个静态线程和一个动态信号量,由于实际使用中动态线程使用较多,做了一些修改,实际烧录后效果与官方例程相同
每隔1.5s释放一次信号量,串口输出一次number数值,间隔输出count release xinhao,共十次