一、应用简介

在RTOS的应用开发中,信号量也是经常使用到的一种用于多任务之间信息同步、资源互斥访问的一种手段,常用于协调多个任务访问同一资源的场景。信号量又分为计数信号量互斥信号量。计数信号量可以表示整体资源数量,当获取一个资源后计数信号量减一,释放一个资源后计数信号量加一,当信号量为0时即表明资源被全部分配导致无法再获取资源,任务可以挂起等待直到有资源可用。互斥信号量是可以理解为只能维护资源数量为1的二值计数信号量(值为0或1),但是互斥信号量又不同于计数信号量,因为它还具有优先级继承的机制(优先级继承机制是RTOS中为了避免出现优先级翻转问题而做的处理方式。简单来说就是如果低优先级持有互斥信号量那么高优先级任务想访问互斥量就会失败而挂起等待互斥量被释放,此时反而是低优先级任务在运行,这就出现了优先级翻转。为了避免该情况RTOS处理方式是把正在持有互斥量运行的低优先级任务的优先级提高到与等待访问互斥资源的高优先级任务同等优先级,这就是优先级继承。等互斥量被释放后RTOS会将该任务恢复到之前的低优先级)。

信号量应用有一个很贴切的举例就是停车场管理。可以把停车场的所有停车位看作计数信号量初始值,当有车辆进入停车场计数信号量值减一,当有车辆离开计数信号量值加一。当值为0时说明没有空车位了,外面车辆无法再停进来,需要等到有空车位出现(计数信号量非0)。停车场中的每一个停车位又可以用互斥信号量来表示,当有车辆占据某车位时表明该车辆持有该车位的互斥信号量,其他车辆无法再停在该车位(如果有人非要杠说万一车位大可以停2个小车那我莫法哈哈哈)。当该车位车辆离开后表明释放该车位的互斥信号量,此时其他车辆可以停入该车位。

二、API简介

下面列出ThreadX中使用信号量常用到的函数

1、创建计数信号量

  • 描述

    此服务为线程间同步创建一个计数信号量。初始信号量计数被指定为输入参数

  • 参数

    • semaphore_ptr 指向信号量控制块的指针
    • name_ptr 指向信号量名称的指针
    • initial_count 指定此信号量的初始计数。 合法值的范围是0x00000000至0xFFFFFFFF
  • 返回值

    • TX_SUCCESS (0x00) 成功创建信号量
    • TX_SEMAPHORE_ERROR (0x0C) 无效的信号量指针。指针是 NULL 或者已经创建了信号量
    • TX_CALLER_ERROR (0x13) 该服务的调用者无效
UINT tx_semaphore_create(
TX_SEMAPHORE *semaphore_ptr,
CHAR *name_ptr,
ULONG initial_count);

2、删除计数信号量

  • 描述

    • 此服务删除指定的计数信号量,恢复所有挂起的等待信号量的线程,并返回TX_DELETED状态。

    • 在删除信号量之前,应用程序必须确保完成(或禁用)此信号量的 put_notify 回调。此外,应用程序必须防止再使用已删除的信号量。

  • 参数

    • semaphore_ptr 指向先前创建的信号量的指针
  • 返回值

    • TX_SUCCESS (0x00) 成功删除计数信号量
    • TX_SEMAPHORE_ERROR (0x0C) 无效的计数信号量指针
    • TX_CALLER_ERROR (0x13) 该服务的调用者无效

UINT tx_semaphore_delete(TX_SEMAPHORE *semaphore_ptr);

3、获取计数信号量

  • 描述

    此服务从指定的计数信号量获取一个信号量,指定的信号量计数减少一个。

  • 参数

    • semaphore_ptr 指向先前创建的计数信号量的指针
    • wait_optionTX_NO_WAIT (0x00000000)不等待直接返回获取结果;TX_WAIT_FOREVER (0xFFFFFFFF)一直挂起等待直到获取到信号量;0x00000001 ~ 0xFFFFFFFE指定挂起等待多少个心跳节拍。
  • 返回值

    • TX_SUCCESS (0x00) 成功获取信号量
    • TX_DELETED (0x01) 线程挂起时删除了计数信号量
    • TX_NO_INSTANCE (0x0D) 服务无法检索计数信号量的实例(在指定的等待时间内信号量计数为零)
    • TX_WAIT_ABORTED (0x1A) 被其他线程或计时器或中断打断而导致服务挂起
    • TX_SEMAPHORE_ERROR (0x0C) 计数信号量指针无效
    • TX_WAIT_ERROR (0x04) 在非线程调用中指定了TX_NO_WAIT以外的等待选项
UINT tx_semaphore_get(
TX_SEMAPHORE *semaphore_ptr,
ULONG wait_option);

4、获取计数信号量信息

  • 描述

    该服务检索有关指定信号量的信息

  • 参数

    参数为TX_NULL表示不获取该参数的信息

    • semaphore_ptr 指向信号量控制块的指针
    • name 指向信号量名称的指针的目标指针
    • current_value 指向当前信号量计数的目标的指针
    • first_suspended 指向这个信号量挂起列表中第一个线程的指针
    • suspended_count 指向当前挂起在此信号量上的线程数的指针
    • next_semaphore 指向下一个创建的信号量指针的目标指针
  • 返回值

    • TX_SUCCESS (0x00) 获取信息成功
    • TX_SEMAPHORE_ERROR (0x0C) 无效的信号量指针
UINT tx_semaphore_info_get(
TX_SEMAPHORE *semaphore_ptr,
CHAR **name, ULONG *current_value,
TX_THREAD **first_suspended,
ULONG *suspended_count,
TX_SEMAPHORE **next_semaphore);

5、增加计数信号量

  • 描述

    • 该服务将指定的信号量计数加一。
    • 如果在信号量为0xFFFFFFFF时调用此服务,则新的put操作将导致信号量重置为零。
  • 参数
    • semaphore_ptr 指向创建的计数信号量控制块的指针
  • 返回值
    • TX_SUCCESS (0x00) 成功放置信号量
    • TX_SEMAPHORE_ERROR (0x0C) 指向信号量的指针无效

UINT tx_semaphore_put(TX_SEMAPHORE *semaphore_ptr);

6、增加指定上限的计数信号量

  • 描述

    该服务将指定的计数信号量增加一个。 如果计数信号量的当前值大于或等于指定的上限,则不会增加信号量,并且将返回TX_CEILING_EXCEEDED错误。

  • 参数

    • semaphore_ptr 指向先前创建的信号量的指针
    • ceiling 信号量所允许的上限值(有效值范围是1到0xFFFFFFFF)
  • 返回值

    • TX_SUCCESS (0x00) 操作成功
    • TX_CEILING_EXCEEDED (0x21) Put请求超过上限
    • TX_INVALID_CEILING (0x22) 为上限提供了无效值零
    • TX_SEMAPHORE_ERROR (0x0C) 无效的信号量指针
UINT tx_semaphore_ceiling_put(
TX_SEMAPHORE *semaphore_ptr,
ULONG ceiling);

7、创建互斥信号量

  • 描述

    此服务为线程间互斥创建互斥体以保护资源。

  • 参数

    • mutex_ptr 指向互斥量控制块的指针
    • name_ptr 指向互斥量名称的指针
    • priority_inherit 指定此互斥对象是否支持优先级继承。 如果此值为TX_INHERIT,则支持优先级继承。 如果指定TX_NO_INHERIT,则此互斥锁不支持优先级继承。
  • 返回值

    • TX_SUCCESS (0x00) 成功创建信号量
    • TX_MUTEX_ERROR (0x1C)无效的互斥指针。 指针为NULL或互斥体已创建
    • TX_CALLER_ERROR (0x13) 该服务的调用者无效
    • TX_INHERIT_ERROR (0x1F) 无效的优先级继承参数
UINT tx_mutex_create(
TX_MUTEX *mutex_ptr,
CHAR *name_ptr,
UINT priority_inherit);

8、删除互斥信号量

  • 描述

    • 该服务将删除指定的互斥信号量。 恢复所有等待互斥的已暂停线程,并返回TX_DELETED返回状态。

    • 应用程序应防止使用已删除的互斥信号量

  • 参数

    • mutex_ptr 指向先前创建的互斥体的指针
  • 返回值

    • TX_SUCCESS (0x00) 操作成功
    • TX_MUTEX_ERROR (0x1C) 无效的互斥体指针
    • TX_CALLER_ERROR (0x13) 该服务的调用者无效

UINT tx_mutex_delete(TX_MUTEX *mutex_ptr);

9、获取互斥信号量

  • 描述

    • 该服务尝试获取指定互斥锁的独占所有权。 如果调用线程已经拥有互斥锁,则内部计数器将递增,并返回成功状态
    • 如果互斥锁由另一个线程拥有,并且该线程具有更高的优先级,并且在互斥锁创建时指定了优先级继承,则优先级较低的线程的优先级将暂时提高到调用线程的优先级。
    • 拥有互斥量的低优先级线程的优先级在互斥体所有权期间绝对不能由外部线程修改
  • 参数
    • mutex_ptr 指向先前创建的互斥体的指针
    • wait_optionTX_NO_WAIT (0x00000000)不等待直接返回获取结果;TX_WAIT_FOREVER (0xFFFFFFFF)一直挂起等待直到获取到信号量;0x00000001 ~ 0xFFFFFFFE指定挂起等待多少个心跳节拍。
  • 返回值
    • TX_SUCCESS (0x00) 操作成功
    • TX_DELETED (0x01) 线程暂停时互斥体被删除
    • TX_NOT_AVAILABLE (0x1D) 服务无法在指定的等待时间内获得互斥锁的所有权
    • TX_WAIT_ABORTED (0x1A) 被其他线程或计时器或中断打断而导致服务挂起
    • TX_MUTEX_ERROR (0x1C) 无效的互斥体指针
    • TX_WAIT_ERROR (0x04) 在非线程调用中指定了TX_NO_WAIT以外的等待选项
    • TX_CALLER_ERROR (0x13) 该服务的调用者无效
UINT tx_mutex_get(
TX_MUTEX *mutex_ptr,
ULONG wait_option);

10、释放互斥信号量

  • 描述

    • 此服务将释放互斥信号量
    • 如果在创建互斥对象时选择了优先级继承,那么释放线程的优先级将恢复到它最初获得互斥对象所有权时的优先级。在拥有互斥对象期间对释放线程所做的任何其他优先级更改都可能被撤消。
  • 参数
    • mutex_ptr 指向先前创建的互斥体的指针
  • 返回值
    • TX_SUCCESS (0x00) 操作成功
    • TX_NOT_OWNED (0x1E) 互斥对象不归调用者所有
    • TX_MUTEX_ERROR (0x1C) 无效的互斥体指针
    • TX_CALLER_ERROR (0x13) 该服务的调用者无效

UINT tx_mutex_put(TX_MUTEX *mutex_ptr);

三、实例演示

  • 该实例用到的硬件资源:一个串口、两个按键、一个LED
  • 创建一个计数信号量,指定数量上限为3,KEY1申请计数信号量,KEY2增加计数信号量
  • 创建一个互斥信号量,用于KEY1、KEY2互斥,即两个按键不能同时按下
  • 创建2个任务,一个用于KEY1任务,一个用于KEY2任务,
#define DEMO_STACK_SIZE         (2 * 1024)
#define DEMO_BYTE_POOL_SIZE (32 * 1024) TX_THREAD thread_0;
TX_THREAD thread_1; TX_BYTE_POOL byte_pool_0;
UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; TX_MUTEX tx_semaphore_mutex; // 互斥信号量
TX_SEMAPHORE tx_semaphore_count; // 计数信号量
void tx_application_define(void *first_unused_memory)
{
UINT status;
CHAR *pointer = TX_NULL; /* 创建互斥信号量 */
status = tx_mutex_create(&tx_semaphore_mutex,"tx_semaphore_mutex",TX_NO_INHERIT);
if (TX_SUCCESS != status)
{
// 创建失败处理
}
/* 创建计数信号量 */
status = tx_semaphore_create(&tx_semaphore_count, "tx_semaphore_count", 3);
if (TX_SUCCESS != status)
{
// 创建失败处理
} /* Create a byte memory pool from which to allocate the thread stacks. */
tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); /* Allocate the stack for thread 0. */
tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create the main thread. */
tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
pointer, DEMO_STACK_SIZE,
1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack for thread 1. */
tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
/* Create threads 1 */
tx_thread_create(&thread_1, "thread 1", thread_1_entry, 0,
pointer, DEMO_STACK_SIZE,
2, 2, TX_NO_TIME_SLICE, TX_AUTO_START);
}
void    thread_0_entry(ULONG thread_input)
{
UINT status, key_flag = 0; CHAR *name;
ULONG current_value;
TX_THREAD *first_suspended;
ULONG suspended_count;
TX_SEMAPHORE *next_semaphore; while(1)
{
if (0 == key_flag)
{
if (GPIO_PIN_SET == HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin))
{
key_flag = 1;
/*按键按下,获取互斥信号量*/
status = tx_mutex_get(&tx_semaphore_mutex, TX_NO_WAIT);
if (TX_SUCCESS == status)
{
/*获取计数信号量*/
if (TX_SUCCESS == tx_semaphore_get(&tx_semaphore_count, TX_NO_WAIT))
{
HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
/*获取计数信号量信息*/
tx_semaphore_info_get(&tx_semaphore_count,
&name,
&current_value,
&first_suspended,
&suspended_count,
&next_semaphore);
printf("key1 pressed, current count semaphore is %d\r\n",(int)current_value);
}
else
{
printf("key1 failed to get count semaphore\r\n");
}
}
else
{
printf("key1 failed to get mutex semaphore\r\n");
}
}
}
else
{
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin))
{
key_flag = 0;
/*按键松开,释放互斥信号量*/
tx_mutex_put(&tx_semaphore_mutex);
}
} tx_thread_sleep(20);
}
}
void    thread_1_entry(ULONG thread_input)
{
UINT status, key_flag = 0;
ULONG current_value; while(1)
{
if (0 == key_flag)
{
if (GPIO_PIN_SET == HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin))
{
key_flag = 1;
/*按键按下,获取互斥信号量*/
status = tx_mutex_get(&tx_semaphore_mutex, TX_NO_WAIT);
if (TX_SUCCESS == status)
{
/*计数信号量加一,指定上限为3*/
//tx_semaphore_put(&tx_semaphore_count);
tx_semaphore_ceiling_put(&tx_semaphore_count,3);
/*获取计数信号量信息*/
tx_semaphore_info_get(&tx_semaphore_count,
TX_NULL,
&current_value,
TX_NULL,
TX_NULL,
TX_NULL);
printf("key2 pressed, current count semaphore is %d\r\n",(int)current_value);
}
else
{
printf("key2 failed to get mutex semaphore\r\n");
}
}
}
else
{
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin))
{
key_flag = 0;
/*按键松开,释放互斥信号量*/
tx_mutex_put(&tx_semaphore_mutex);
}
} tx_thread_sleep(20);
}
}

串口打印演示结果如下


[20:51:33.330]收←◆key1 pressed, current count semaphore is 2 [20:51:34.034]收←◆key1 pressed, current count semaphore is 1 [20:51:34.556]收←◆key1 pressed, current count semaphore is 0 [20:51:34.939]收←◆key1 failed to get count semaphore [20:51:35.522]收←◆key1 failed to get count semaphore [20:51:37.004]收←◆key2 pressed, current count semaphore is 1 [20:51:37.488]收←◆key2 pressed, current count semaphore is 2 [20:51:37.851]收←◆key2 pressed, current count semaphore is 3 [20:51:38.234]收←◆key2 pressed, current count semaphore is 3 [20:51:38.556]收←◆key2 pressed, current count semaphore is 3 [20:51:44.925]收←◆key1 pressed, current count semaphore is 2 [20:51:46.579]收←◆key2 failed to get mutex semaphore [20:51:52.102]收←◆key2 pressed, current count semaphore is 3 [20:51:52.987]收←◆key1 failed to get mutex semaphore

ThreadX——IPC应用之信号量的更多相关文章

  1. Linux进程间通信(IPC)之信号量

    [Linux]进程间通信(IPC)之信号量详解与测试用例 2017年03月22日 17:28:50 阅读数:2255 学习环境centos6.5 Linux内核2.6 进程间通信概述 1. 进程通信机 ...

  2. Linux IPC System V 信号量

    模型 #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> ftok() //获取key ...

  3. IPC 进程间通信方式——信号量

    信号量 本质上是共享资源的数目,用来控制对共享资源的访问. 用于进程间的互斥和同步 每种共享资源对应一个信号量,为了便于大量共享资源的操作引入了信号量集,可对多对信号量一次性操作.对信号量集中所有的操 ...

  4. 五十一、进程间通信——System V IPC 之进程信号量

    51.1 进程信号量 51.1.1 信号量 本质上就是共享资源的数目,用来控制对共享资源的访问 用于进程间的互斥和同步 每种共享资源对应一个信号量,为了便于大量共享资源的操作引入了信号量集,可对所有信 ...

  5. Linux下IPC中的信号量PV操作

    代码如下所示,两边对照查看程序!(左图为先运行进程 右图为后运行进程)    运行的效果就是:当左边的进程检测到EOF,释放资源V操作之后,右边的进程会迅速的执行对应的printf的操作! 所有代码文 ...

  6. ThreadX——IPC应用之消息队列

    作者:zzssdd2 E-mail:zzssdd2@foxmail.com 一.应用简介 消息队列是RTOS中常用的一种数据通信方式,常用于任务与任务之间或是中断与任务之间的数据传递.在裸机系统中我们 ...

  7. Unix IPC之Posix信号量实现生产者消费者

    采用多生产者,多消费者模型. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /**  * 生产者  */ P(nempty); P(mutex); // 写入一个 ...

  8. Linux IPC 之信号量

    信号量(也叫信号灯)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语. 信号量是进程/线程同步的一种方式,有时候我们需要保护一段代码,使它每次只能被一个执行进程/线程运行,这种工作就需 ...

  9. UNIX:高级环境编程 - 第十五章 IPC:进程间通信

    IPC(InterProcess Communication)进程间通信.为啥没有进程间通信,这是因为进程间都是同步的关系,不需要通信. 1.管道 1.1管道特点: (1)半双工的(即数据只能在一个方 ...

随机推荐

  1. mysql_用命令行备份数据库

    MySQL数据库使用命令行备份|MySQL数据库备份命令 例如: 数据库地址:127.0.0.1 数据库用户名:root 数据库密码:pass 数据库名称:myweb 备份数据库到D盘跟目录 mysq ...

  2. matlab 数组操作作业

    写出下列语句的计算结果及作用 1.A= [2 5 7 3 1 3 4 2];    创建二维数组并赋值 2.[rows, cols] = size(A);    ​把A的尺寸赋值给数组,rows为行, ...

  3. a^b(取模运算)

    a^b(sdtbu oj 1222) Description 对于任意两个正整数a,b(0 <= a, b < 10000)计算ab各位数字的和的各位数字的和的各位数字的和的各位数字的和. ...

  4. Shodan搜索引擎详解及Python命令行调用

    shodan常用信息搜索命令 shodan配置命令 shodan init T1N3uP0Lyeq5w0wxxxxxxxxxxxxxxx //API设置 shodan信息收集 shodan myip ...

  5. python-网络安全编程第二天(文件操作)

    前言 才吃完火锅嘿嘿,吃完把今天所学的内容写个博客当做笔记用哈哈! 文件操作 f=open("test.txt",w)直接打开一个文件,如果文件不存在则创建文件open模式w:以写 ...

  6. cmd编译java代码为什么总是说找不到main方法;请园子里大神指点迷津!!!

    编写源代码如下: cmd,编译路径:E: cd Notepad cd src javac Character.java jvav Character 运行结果: 实在是找不到问题点,请评论区给予指导啊 ...

  7. 这几种实现线程的方法你一定要知道,月薪20k以上的面试都会问到

    实现线程的三种方式总结 最近有看到Java线程的实现相关问题,在此对线程实现方式做一个小小的总结,当做笔记,便于日后查看. 平时常用的线程方式有三种: (1).继承Thread类,并重写其run()方 ...

  8. Android系统添加key和keypad

    平台:MTK 一.添加一个按键 1.在DCT tool keypad list 文件增加新按键的选项alps\mediatek\source\dct\Keypad_YuSu.cmp中添加新键,如SMS ...

  9. 如何用ABBYY解决文档图像存在缺陷,OCR 准确性低的问题

    扭曲的文本行.歪斜.噪声及扫描图像和数码照片中常见的其他缺陷可能会降低识别质量.ABBYY FineReader,提供各种 自动和手动工具去除这些缺陷. 如何手动编辑图像 如果您禁用了自动预处理功能或 ...

  10. CorelDRAW快速去除图片背景颜色

    当我们需要从网上借助一些素材图片在CorelDRAW中运用时,往往需要去掉图片的背景颜色.本文小编分享CDR中如何快速去除图片背景颜色的方法,通过此方法可以做简单的照片抠图.合成. 1. 打开Core ...