信号和信号量
本文档根据《嘉楠K230开发手册》V1.0(2024-11-30)整理。正文、表格、示例代码与插图均来自原始手册。
信号和信号量,没有任何关系。之所以把它们放在同一章里,完全是因为很多人会混淆,那么我们干脆同时讲解它们。
信号的本质是软中断,是线程层面对中断机制的一种模拟:
- 线程平时执行自己的函数
- 别的线程或者中断服务程序给线程发信号
- 线程当前的执行被打断,线程转而去执行信号处理函数,执行完信号处理函数后再继续运行之前的代码
如果想要使用信号,需要在rtconfig.h中定义**#define RT_USING_SIGNALS**。
而信号量就像队列、邮箱一样,是用来传递信息的:
- 队列:传递各类大小的数据
- 邮箱:传递多个32位的数据
- 信号量:传递1个数值
消息队列、邮箱用于传输多个数据,但是有时候我们只需要传递状态,这个状态值需要用一个数值表示,比如:
- 卖家:做好了1个包子,包子数量加1
- 买家:买了1个包子,包子数量减1
- 这个停车位我占了,停 车位减1
- 我开车走了,停车位加1
在这种情况下我们只需要维护一个数值,使用信号量效率更高、更节省内存
本章涉及如下内容:
- 信号的工作机制
- 怎么使用信号
- 信号量的工作机制
- 怎么使用信号量
信号的特性
信号的工作机制
信号的发送者、接收者都是线程:线程发信号给线程,尚不支持中断发信号给线程。
线程对收到的信号,有三类处理方式:
- 第一类:类似中断处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数处理
- 第二类:忽略某个信号,对该信号不处理
- 第三类:对该信号的处理,使用系统的默认方式
- 怎么使用这些处理方式?就是给信号安装处理函数:
rt_signal_install(SIGUSR1, my_signal_handler); /* 给信号SIGUSR1安装我们自己的处理函数*/rt_signal_install(SIGUSR1, SIG_IGN); /* 不处理信号SIGUSR1 */rt_signal_install(SIGUSR1, SIG_DFL); /* 给信号SIGUSR1安装默认的处理函数,只是打印 */
RT-Thread根据POSIX标准定义信号集,大部分信号被系统所用,用户程序只能使用SIGUSR1(10)和 SIGUSR2(12)。
假设有两个线程,线程1接收信号,线程2发送信号,用法如下:
- 线程1先安装信号并设置对信号的处理方式,然后解除阻塞
/* 安装信号,自定义处理函数 */rt_signal_install(SIGUSR1, my_signal_handler); /* 解除阻塞 */rt_signal_unmask(SIGUSR1);
- 线程2发送信号,触发线程1进行处理
rt_thread_kill(thread1, SIGUSR1); //向线程1发送信号SIGUSR1
- 线程1如果在挂起状态收到信号
- 线程1被唤醒
- 线程1先调用信号处理函数
- 再继续运行之前的代码
- 线程1如果在就绪状态收到信号
- 线程1再次运行时,先调用信号处理函数
- 再继续运行之前的代码
- 线程2给自己发信号:rt_thread_kill(thread2, SIGUSR1)
- 在rt_thread_kill()函数内部直接调用信号处理函数
信号函数
使用信号时,需要先安装、屏蔽/使能、发送和等待。

安装
如果线程需要处理某一个信号,就需要现在线程中安装该信号。
安装信号的函数原型如下:
/* 安装一个信号,返回安装结果。 * 此函数有两个参数,分别为信号值和信号值的处理方法 * signo:信号值(SIGUSR1 或 SIGUSR2) * handler:处理方式(SIG_IGN、SIG_DFL、自定义处理函数) * 返回值: 成功返回handler值,错误返回SIG_ERR */rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler);
第2个参数可以传入我们提供的函数,也可以:
- 传入SIG_DFL,即使用系统的默认方式,系统会调用默认的处理函数**_signal_default_handler()**,它只是打印
- 传入SIG_IGN