rtthread信号 基础讲解
当“信号”突然到来时操作系统会强行暂停接收线程当前的常规工作逼迫它立刻去执行一个预先写好的“紧急处理函数”。等紧急函数执行完线程再回到刚才被暂停的地方继续运行。收到信号的线程对各种信号有不同的处理方法。处理方法可以分为三类第一种是类似中断的处理程序对于需要处理的信号线程可以指定处理函数由该函数来处理。例子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 会瞬间被系统从内存中抹除灰飞烟灭。例子1Thread 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发射自定义信号 1SIGUSR1 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; }1.rt_signal_install(SIGUSR1, thread1_signal_handler)物理作用 操作系统内部为每个线程维护了一个“信号配置表”。调用这个函数相当于往 Thread 1 的配置表里写了一条规则“当接收到编号为SIGUSR1的信号时目标函数指针是thread1_signal_handler的物理地址。” 这仅仅是登记并没有开启接收。2.rt_signal_unmask(SIGUSR1)修改寄存器掩码位物理作用 每个线程都有一个 32 位的掩码变量用来决定放行还是屏蔽某个信号。unmask就是把代表SIGUSR1的那个二进制位清零告诉内核“允许外部的SIGUSR1信号打断我。”3.rt_thread_kill(thread1_ptr, SIGUSR1)极其重要的避坑 很多初学者看到kill这个词以为是把 Thread 1 杀掉/销毁。绝对不是 在 C 语言和操作系统底层kill的严格语义是**“发送信号Send Signal”**。物理作用 Thread 2 告诉内核“去修改 Thread 1 的栈内存强行塞入刚才登记的thread1_signal_handler函数地址让它立刻去执行。”4.thread1_signal_handler()代码位置 独立定义的普通函数。物理作用 虽然它看起来像一个独立的代码块但当它被触发执行时它消耗的是 Thread 1 的那 2048 字节的栈空间。它是寄生在 Thread 1 的上下文中运行的。