rtthread信号 基础讲解
信号处理是操作系统中线程通信的重要机制。当线程收到信号时,系统会中断当前执行流程,转而执行预设的信号处理函数。处理方法分为三类:自定义处理函数、忽略信号或采用系统默认处理。在RT-Thread中,通过rt_signal_install()注册处理函数,rt_signal_unmask()解除信号屏蔽,rt_thread_kill()发送信号。系统默认处理可能直接终止接收线程。示例展示了高优先级线程
信号类似于中断,只不过是软件层面的。
当“信号”突然到来时,操作系统会强行暂停接收线程当前的常规工作,逼迫它立刻去执行一个预先写好的“紧急处理函数”。等紧急函数执行完,线程再回到刚才被暂停的地方,继续运行。
收到信号的线程对各种信号有不同的处理方法。处理方法可以分为三类:
-
第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。(例子1)
-
第二种方法是,忽略某个信号,对该信号不做任何处理,就象未发生过一样。
-
第三种方法是,对该信号的处理保留系统的默认值。
方法二:
rt_signal_install(SIGUSR1, (rt_sighandler_t)SIG_IGN);
-
当线程调用
kill向你发射SIGUSR1时,内核介入并查表。 -
内核判断逻辑: 内核一看,目标地址是
SIG_IGN(即1)。内核立刻判定:“目标线程明确要求丢弃此信号。” -
执行结果: 内核直接把这个待处理的信号标志位清零,然后默默退出。目标线程不会发生任何上下文切换,CPU 寄存器不会被保存,线程的常规执行流不会受到哪怕 1 微秒的停顿。 这是一种在内核层面就被直接拦截的“物理级屏蔽”。
方法三:
rt_signal_install(SIGUSR1, (rt_sighandler_t)SIG_DFL);
-
触发瞬间: 当 Thread 2 再次发射
SIGUSR1时,内核查表。 -
内核判断逻辑: 内核一看,目标地址是
SIG_DFL(即0)。内核判定:“目标线程没有自定义处理方案,启用操作系统内置的出厂预案。” -
执行结果: 内核会去调用 RT-Thread 内部写死的一个默认处理函数(通常叫
_signal_default_handler)。极度危险的后果: 在 RT-Thread 中,对于绝大多数未被定义的常规信号,操作系统的“默认预案”非常残暴——直接终止(Delete/Kill)接收到该信号的线程! Thread 1 会瞬间被系统从内存中抹除,灰飞烟灭。
主要的函数:
1. 安装信号处理函数:rt_signal_install
-
作用: 告诉内核,如果本线程收到了某个信号(比如
SIGUSR1),应该跳到哪个函数去执行。 -
参数:
-
sig:信号编号(如SIGUSR1)。 -
handler:你自己写的一个函数地址(回调函数)。
-
-
返回值: 返回原先旧的信号处理函数地址。
2. 解除信号屏蔽:rt_signal_unmask
-
作用: 默认情况下,线程是不理会信号的。必须调用此函数,告诉内核:“我现在准备好接收这个信号了”。
-
参数:
sig(信号编号)。
3. 发送信号:rt_signal_send
-
作用: 向指定的目标线程“投掷”一个信号。
-
参数:
-
thread:目标线程的句柄。 -
sig:发送哪个信号。
-
-
返回值:
RT_EOK或错误码。
例子1:
-
Thread 1(接收者):负责安装信号处理函数、解除信号屏蔽,并在
while(1)中执行常规打印任务。 -
Thread 2(发送者):延时 3.5 秒后,向 Thread 1 发送信号。
-
信号处理函数:Thread 1 收到信号后,被强制打断去执行的 “紧急任务”(打印警告)。
#include <rtthread.h>
#define THREAD_PRIORITY 10
#define THREAD_TIMESLICE 5
rt_thread_t thread1_ptr;
rt_thread_t thread2_ptr;
// =========================================================
// 1. 定义信号处理函数(这就是被强行跳转过来执行的“紧急任务”)
// =========================================================
void thread1_signal_handler(int sig)
{
// 当这个函数执行时,Thread 1 原本的 while(1) 循环已经被系统强行按下了暂停键
rt_kprintf("\n>>> [Signal Handler] 警告!收到信号编号: %d,正在执行紧急打断任务! <<<\n\n", sig);
// 函数执行完 return 后,Thread 1 才会恢复之前的状态继续运行
}
// =========================================================
// 接收线程(充当“被无情打断”的角色)
// =========================================================
void thread1_entry(void *parameter)
{
// 2. 安装信号:向操作系统登记预案。
// 意思是:“如果有人给我发 SIGUSR1 这个信号,请让我去执行 thread1_signal_handler 函数”
rt_signal_install(SIGUSR1, thread1_signal_handler);
// 3. 解除屏蔽:默认是不接收信号的,必须手动打开开关。
rt_signal_unmask(SIGUSR1);
int count = 0;
while (1)
{
// 线程 1 正在专心致志地做自己的常规任务
rt_kprintf("Thread 1: 正在正常运行常规逻辑... count = %d\n", count++);
// 使用延时模拟一段需要耗费时间的常规代码
rt_thread_mdelay(1000);
}
}
// =========================================================
// 发送线程(充当“开火触发”的角色)
// =========================================================
void thread2_entry(void *parameter)
{
// 先故意等 3.5 秒,让 Thread 1 先安心地打印几行常规任务
rt_thread_mdelay(3500);
rt_kprintf("\nThread 2: 准备发射信号,强行打断 Thread 1 的常规逻辑!\n");
// 4. 发送信号:核心 API。
// 参数1:精准瞄准目标线程的句柄(thread1_ptr)
// 参数2:发射自定义信号 1(SIGUSR1)
rt_thread_kill(thread1_ptr, SIGUSR1);
while (1)
{
// 发送完毕后,自己进入无尽的休眠
rt_thread_mdelay(10000);
}
}
int main(void)
{
// 创建并启动 Thread 1
thread1_ptr = rt_thread_create("t1", thread1_entry, RT_NULL, 2048, THREAD_PRIORITY, THREAD_TIMESLICE);
rt_thread_startup(thread1_ptr);
// 创建并启动 Thread 2
thread2_ptr = rt_thread_create("t2", thread2_entry, RT_NULL, 2048, THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(thread2_ptr);
return 0;
}

更多推荐



所有评论(0)