信号类似于中断,只不过是软件层面的。

当“信号”突然到来时,操作系统会强行暂停接收线程当前的常规工作,逼迫它立刻去执行一个预先写好的“紧急处理函数”。等紧急函数执行完,线程再回到刚才被暂停的地方,继续运行。

收到信号的线程对各种信号有不同的处理方法。处理方法可以分为三类:

  • 第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。(例子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;
}

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐