信号量相关接口
创建信号量
当创建一个信号量时,内核首先创建一个信号量控制块,然后对该控制块进行基本的初始化工作,创建信号量使用下面的函数接口:
- rt_sem_t rt_sem_create (const char* name, rt_uint32_t value, rt_uint8_t flag);
当调用这个函数时,系统将先分配一个semaphore对象,并初始化这个对象,然后初始化IPC对象以及与semaphore相关的部分。在创建信号量指定的参数中,信号量标志参数决定了当信号量不可用时,多个线程等待的排队方式。当选择FIFO方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择PRIO(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。
函数参数
- 参数 描述
- name 信号量名称;
- value 信号量初始值;
- flag 信号量标志,取值可以使用如下类型:
- #define RT_IPC_FLAG_FIFO 0x00 /* IPC参数采用FIFO方式*/
- #define RT_IPC_FLAG_PRIO 0x01 /* IPC参数采用优先级方式*/
函数返回
创建成功返回创建的信号量控制块指针;否则返回RT_NULL。
创建信号量的例程如下例所示:
- /*
- * 程序清单:动态信号量
- *
- * 这个例子中将创建一个动态信号量(初始值为0)及一个动态线程,在这个动态线程中
- * 将试图采用超时方式去持有信号量,应该超时返回。然后这个线程释放一次信号量,
- * 并在后面继续采用永久等待方式去持有信号量, 成功获得信号量后返回。
- */
- #include <rtthread.h>
- #include "tc_comm.h"
- /* 指向线程控制块的指针 */
- static rt_thread_t tid = RT_NULL;
- /* 指向信号量的指针 */
- static rt_sem_t sem = RT_NULL;
- /* 线程入口 */
- static void thread_entry(void* parameter)
- {
- rt_err_t result;
- rt_tick_t tick;
- /* 获得当前的OS Tick */
- tick = rt_tick_get();
- /* 试图持有一个信号量,如果10个OS Tick依然没拿到,则超时返回 */
- result = rt_sem_take(sem, 10);
- if (result == -RT_ETIMEOUT)
- {
- /* 判断是否刚好过去10个OS Tick */
- if (rt_tick_get() - tick != 10)
- {
- /* 如果失败,则测试失败 */
- tc_done(TC_STAT_FAILED);
- rt_sem_delete(sem);
- return;
- }
- rt_kprintf("take semaphore timeout\n");
- }
- else
- {
- /* 因为并没释放信号量,应该是超时返回,否则测试失败 */
- tc_done(TC_STAT_FAILED);
- rt_sem_delete(sem);
- return;
- }
- /* 释放一次信号量 */
- rt_sem_release(sem);
- /* 继续持有信号量,并永远等待直到持有到信号量 */
- result = rt_sem_take(sem, RT_WAITING_FOREVER);
- if (result != RT_EOK)
- {
- /* 返回不正确,测试失败 */
- tc_done(TC_STAT_FAILED);
- rt_sem_delete(sem);
- return;
- }
- /* 测试成功 */
- tc_done(TC_STAT_PASSED);
- /* 删除信号量 */
- rt_sem_delete(sem);
- }
- int semaphore_dynamic_init()
- {
- /* 创建一个信号量,初始值是0 */
- sem = rt_sem_create("sem", 0, RT_IPC_FLAG_FIFO);
- if (sem == RT_NULL)
- {
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- return 0;
- }
- /* 创建线程 */
- tid = rt_thread_create("thread",
- thread_entry, RT_NULL, /* 线程入口是thread_entry, 参数RT_NULL */
- THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
- if (tid != RT_NULL)
- rt_thread_startup(tid);
- else
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- return 0;
- }
- #ifdef RT_USING_TC
- static void _tc_cleanup()
- {
- /* 调度器上锁,上锁后,将不再切换到其他线程,仅响应中断 */
- rt_enter_critical();
- /* 删除线程 */
- if (tid != RT_NULL && tid->stat != RT_THREAD_CLOSE)
- {
- rt_thread_delete(tid);
- /* 删除信号量 */
- rt_sem_delete(sem);
- }
- /* 调度器解锁 */
- rt_exit_critical();
- /* 设置TestCase状态 */
- tc_done(TC_STAT_PASSED);
- }
- int _tc_semaphore_dynamic()
- {
- /* 设置TestCase清理回调函数 */
- tc_cleanup(_tc_cleanup);
- semaphore_dynamic_init();
- /* 返回TestCase运行的最长时间 */
- return 100;
- }
- /* 输出函数命令到finsh shell中 */
- FINSH_FUNCTION_EXPORT(_tc_semaphore_dynamic, a dynamic semaphore example);
- #else
- /* 用户应用入口 */
- int rt_application_init()
- {
- semaphore_dynamic_init();
- return 0;
- }
- #endif
删除信号量
系统不再使用信号量时,可通过删除信号量以释放系统资源。删除信号量使用下面的函数接口:
- rt_err_t rt_sem_delete (rt_sem_t sem);
调用这个函数时,系统将删除这个信号量。如果删除该信号量时,有线程正在等待该信号量,那么删除操作会先唤醒等待在该信号量上的线程(等待线程的返回值是-RT_ERROR),然后再释放信号量的内存资源。
- 参数 描述
- sem rt_sem_create创建处理的信号量对象。
函数返回
RT_EOK
初始化信号量
对于静态信号量对象,它的内存空间在编译时期就被编译器分配出来,放在数据段或ZI段上,此时使用信号量就不再需要使用rt_sem_create接口来创建它,而只需在使用前对它进行初始化即可。初始化信号量对象可使用下面的函数接口:
- rt_err_t rt_sem_init (rt_sem_t sem, const char* name, rt_uint32_t value, rt_uint8_t flag);
当调用这个函数时,系统将对这个semaphore对象进行初始化,然后初始化IPC对象以及与semaphore相关的部分。在初始化信号量指定的参数中,信号量标志参数决定了当信号量不可用时,多个线程等待的方式。当选择FIFO方式时,那么等待线程队列将按照先进先出的方式排队,先进入的线程将先获得等待的信号量;当选择PRIO(优先级等待)方式时,等待线程队列将按照优先级进行排队,优先级高的等待线程将先获得等待的信号量。
函数参数
- 参数 描述
- sem 信号量对象的句柄;
- name 信号量名称;
- value 信号量初始值;
- flag 信号量标志。
- #define RT_IPC_FLAG_FIFO 0x00 /* IPC参数采用FIFO方式*/
- #define RT_IPC_FLAG_PRIO 0x01 /* IPC参数采用优先级方式*/
函数返回
RT_EOK
初始化信号量的例程如下例所示:
- /*
- * 程序清单:静态信号量
- *
- * 这个例子中将创建一个静态信号量(初始值为0 )及一个静态线程,在这个静态线程中
- * 将试图采用超时方式去获取信号量,应该超时返回。然后这个线程释放一次信号量,并
- * 在后面继续采用永久等待方式去获取信号量, 成功获得信号量后返回。
- */
- #include <rtthread.h>
- #include "tc_comm.h"
- /* 线程控制块及栈 */
- static struct rt_thread thread;
- static rt_uint8_t thread_stack[THREAD_STACK_SIZE];
- /* 信号量控制块 */
- static struct rt_semaphore sem;
- /* 线程入口 */
- static void thread_entry(void* parameter)
- {
- rt_err_t result;
- rt_tick_t tick;
- /* 获得当前的OS Tick */
- tick = rt_tick_get();
- /* 试图持有信号量,最大等待10个OS Tick后返回 */
- result = rt_sem_take(&sem, 10);
- if (result == -RT_ETIMEOUT)
- {
- /* 超时后判断是否刚好是10个OS Tick */
- if (rt_tick_get() - tick != 10)
- {
- tc_done(TC_STAT_FAILED);
- rt_sem_detach(&sem);
- return;
- }
- rt_kprintf("take semaphore timeout\n");
- }
- else
- {
- /* 因为没有其他地方释放信号量,所以不应该成功持有信号量,否则测试失败 */
- tc_done(TC_STAT_FAILED);
- rt_sem_detach(&sem);
- return;
- }
- /* 释放一次信号量 */
- rt_sem_release(&sem);
- /* 永久等待方式持有信号量 */
- result = rt_sem_take(&sem, RT_WAITING_FOREVER);
- if (result != RT_EOK)
- {
- /* 不成功则测试失败 */
- tc_done(TC_STAT_FAILED);
- rt_sem_detach(&sem);
- return;
- }
- /* 测试通过 */
- tc_done(TC_STAT_PASSED);
- /* 脱离信号量对象 */
- rt_sem_detach(&sem);
- }
- int semaphore_static_init()
- {
- rt_err_t result;
- /* 初始化信号量,初始值是0 */
- result = rt_sem_init(&sem, "sem", 0, RT_IPC_FLAG_FIFO);
- if (result != RT_EOK)
- {
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- return 0;
- }
- /* 初始化线程1 */
- result = rt_thread_init(&thread, "thread", /* 线程名:thread */
- thread_entry, RT_NULL, /* 线程的入口是thread_entry,参数是RT_NULL*/
- &thread_stack[0], sizeof(thread_stack), /* 线程栈thread_stack */
- THREAD_PRIORITY, 10);
- if (result == RT_EOK) /* 如果返回正确,启动线程1 */
- rt_thread_startup(&thread);
- else
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- return 0;
- }
- #ifdef RT_USING_TC
- static void _tc_cleanup()
- {
- /* 调度器上锁,上锁后,将不再切换到其他线程,仅响应中断 */
- rt_enter_critical();
- /* 执行线程脱离 */
- if (thread.stat != RT_THREAD_CLOSE)
- {
- rt_thread_detach(&thread);
- /* 执行信号量对象脱离 */
- rt_sem_detach(&sem);
- }
- /* 调度器解锁 */
- rt_exit_critical();
- /* 设置TestCase状态 */
- tc_done(TC_STAT_PASSED);
- }
- int _tc_semaphore_static()
- {
- /* 设置TestCase清理回调函数 */
- tc_cleanup(_tc_cleanup);
- semaphore_static_init();
- /* 返回TestCase运行的最长时间 */
- return 100;
- }
- /* 输出函数命令到finsh shell中 */
- FINSH_FUNCTION_EXPORT(_tc_semaphore_static, a static semaphore example);
- #else
- /* 用户应用入口 */
- int rt_application_init()
- {
- thread_static_init();
- return 0;
- }
- #endif
脱离信号量
脱离信号量就是让信号量对象从内核对象管理器中移除掉。脱离信号量使用下面的函数接口:
- rt_err_t rt_sem_detach (rt_sem_t sem);
使用该函数后,内核先唤醒所有挂在该信号量等待队列上的线程,然后将该信号量从内核对象管理器中删除。原来挂起在信号量上的等待线程将获得-RT_ERROR 的返回值。
- 参数 描述
- sem 信号量对象的句柄。
函数返回
RT_EOK
获取信号量
线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值都会减1,获取信号量使用下面的函数接口:
- rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);
在调用这个函数时,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据time参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数time指定的时间内依然得不到信号量,线程将超时返回,返回值是-RT_ETIMEOUT。
函数参数
- 参数 描述
- sem 信号量对象的句柄;
- time 指定的等待时间,单位是操作系统时钟节拍(OS Tick)。
函数返回
成功获得信号量返回RT_EOK;超时依然未获得信号量返回-RT_ETIMEOUT;其他错误返回-RT_ERROR。
无等待获取信号量
当用户不想在申请的信号量上挂起线程进行等待时,可以使用无等待方式获取信号量,无等待获取信号量使用下面的函数接口:
- rt_err_t rt_sem_trytake(rt_sem_t sem);
这个函数与rt_sem_take(sem, 0) 的作用相同,即当线程申请的信号量资源实例不可用的时候,它不会等待在该信号量上,而是直接返回-RT_ETIMEOUT。
函数参数
- 参数 描述
- sem 信号量对象的句柄。
函数返回
成功获取信号量返回RT_EOK;否则返回RT_ETIMEOUT。
释放信号量
当线程完成资源的访问后,应尽快释放它持有的信号量,使得其他线程能获得该信号量。释放信号量使用下面的函数接口:
- rt_err_t rt_sem_release(rt_sem_t sem);
当信号量的值等于零时,并且有线程等待这个信号量时,将唤醒等待在该信号量线程队列中的第一个线程,由它获取信号量。否则将把信号量的值加一。
函数参数
- 参数 描述
- sem 信号量对象的句柄。
函数返回
RT_EOK
下面是一个使用信号量的例程,如下例所示:
- /*
- * 程序清单:生产者消费者例子
- *
- * 这个例子中将创建两个线程用于实现生产者消费者问题
- */
- #include <rtthread.h>
- #include "tc_comm.h"
- /* 定义最大5个元素能够被产生 */
- #define MAXSEM 5
- /* 用于放置生产的整数数组 */
- rt_uint32_t array[MAXSEM];
- /* 指向生产者、消费者在array数组中的读写位置 */
- static rt_uint32_t set, get;
- /* 指向线程控制块的指针 */
- static rt_thread_t producer_tid = RT_NULL;
- static rt_thread_t consumer_tid = RT_NULL;
- struct rt_semaphore sem_lock;
- struct rt_semaphore sem_empty, sem_full;
- /* 生产者线程入口 */
- void producer_thread_entry(void* parameter)
- {
- rt_int32_t cnt = 0;
- /* 运行100次 */
- while( cnt < 100)
- {
- /* 获取一个空位 */
- rt_sem_take(&sem_empty, RT_WAITING_FOREVER);
- /* 修改array内容,上锁 */
- rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
- array[set%MAXSEM] = cnt + 1;
- rt_kprintf("the producer generates a number: %d\n",
- array[set%MAXSEM]);
- set++;
- rt_sem_release(&sem_lock);
- /* 发布一个满位 */
- rt_sem_release(&sem_full);
- cnt++;
- /* 暂停一段时间 */
- rt_thread_delay(50);
- }
- rt_kprintf("the producer exit!\n");
- }
- /* 消费者线程入口 */
- void consumer_thread_entry(void* parameter)
- {
- rt_uint32_t no;
- rt_uint32_t sum = 0;
- /* 第n个线程,由入口参数传进来 */
- no = (rt_uint32_t)parameter;
- while(1)
- {
- /* 获取一个满位 */
- rt_sem_take(&sem_full, RT_WAITING_FOREVER);
- /* 临界区,上锁进行操作 */
- rt_sem_take(&sem_lock, RT_WAITING_FOREVER);
- sum += array[get%MAXSEM];
- rt_kprintf("the consumer[%d] get a number:%d\n",
- no, array[get%MAXSEM]);
- get++;
- rt_sem_release(&sem_lock);
- /* 释放一个空位 */
- rt_sem_release(&sem_empty);
- /* 生产者生产到100个数目,停止,消费者线程相应停止 */
- if (get == 100) break;
- /* 暂停一小会时间 */
- rt_thread_delay(10);
- }
- rt_kprintf("the consumer[%d] sum is %d \n ", no, sum);
- rt_kprintf("the consumer[%d] exit!\n");
- }
- int semaphore_producer_consumer_init()
- {
- /* 初始化3个信号量 */
- rt_sem_init(&sem_lock , "lock", 1, RT_IPC_FLAG_FIFO);
- rt_sem_init(&sem_empty,"empty", MAXSEM, RT_IPC_FLAG_FIFO);
- rt_sem_init(&sem_full , "full", 0, RT_IPC_FLAG_FIFO);
- /* 创建线程1 */
- producer_tid = rt_thread_create("producer",
- producer_thread_entry, /* 线程入口是producer_thread_entry */
- RT_NULL, /* 入口参数是RT_NULL */
- THREAD_STACK_SIZE, THREAD_PRIORITY - 1, THREAD_TIMESLICE);
- if (producer_tid != RT_NULL)
- rt_thread_startup(producer_tid);
- else
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- /* 创建线程2 */
- consumer_tid = rt_thread_create("consumer",
- consumer_thread_entry,/* 线程入口是consumer_thread_entry */
- RT_NULL, /* 入口参数是RT_NULL */
- THREAD_STACK_SIZE, THREAD_PRIORITY + 1, THREAD_TIMESLICE);
- if (consumer_tid != RT_NULL)
- rt_thread_startup(consumer_tid);
- else
- tc_stat(TC_STAT_END | TC_STAT_FAILED);
- return 0;
- }
- #ifdef RT_USING_TC
- static void _tc_cleanup()
- {
- /* 调度器上锁,上锁后,将不再切换到其他线程,仅响应中断 */
- rt_enter_critical();
- /* 删除线程 */
- if (producer_tid != RT_NULL && producer_tid->stat != RT_THREAD_CLOSE)
- rt_thread_delete(producer_tid);
- if (consumer_tid != RT_NULL && consumer_tid->stat != RT_THREAD_CLOSE)
- rt_thread_delete(consumer_tid);
- /* 调度器解锁 */
- rt_exit_critical();
- /* 设置TestCase状态 */
- tc_done(TC_STAT_PASSED);
- }
- int _tc_semaphore_producer_consumer()
- {
- /* 设置TestCase清理回调函数 */
- tc_cleanup(_tc_cleanup);
- semaphore_producer_consumer_init();
- /* 返回TestCase运行的最长时间 */
- return 100;
- }
- /* 输出函数命令到finsh shell中 */
- FINSH_FUNCTION_EXPORT(_tc_semaphore_producer_consumer, producer and consumer example);
- #else
- /* 用户应用入口 */
- int rt_application_init()
- {
- semaphore_producer_consumer_init();
- return 0;
- }
- #endif
在这个例子中,semaphore是作为一种锁的形式存在,当要访问临界资源:ring buffer时,通过持有semaphore 的形式阻止其他线程进入(如果其他线程也打算进入,将在这里被挂起)。