信号

信号#

信号就像⼀个紧急电话。如果你想让你的朋友注意到某件重要的事情,可以打电话通知他。朋友接到电话(信号)后,便可以⽴即采取⾏动或处理相关事宜。

信号的⼯作机制#

  • 发送信号:线程可以向另⼀个线程发送信号。可以让⽬标线程⽴即中断当前⼯作,转⽽处理信号事件。

  • 处理信号:⽬标线程需要先安装信号处理函数,收到信号时会执⾏相应的处理操作

    信号相关接口

1. rt_sem_create

用于动态创建信号量。

rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag);

参数说明:

  • name:信号量的名字(可以为 RT_NULL 表示匿名信号量)。

  • value:信号量的初始值,表示信号量当前持有的资源数量(例如初始值为 0 表示等待事件,为 1 表示互斥量,或者更大值表示资源计数)。

  • flag

    • RT_IPC_FLAG_PRIO:优先级等待方式,等待线程按照优先级顺序排列。

    • RT_IPC_FLAG_FIFO:先入先出等待方式,等待线程按照进入顺序排列。

返回值:

  • 成功:返回信号量的句柄 rt_sem_t

  • 失败:返回 RT_NULL

示例:

/* 创建一个初始值为 0 的信号量,用于同步场景 */
rt_sem_t sync_sem = rt_sem_create("sync_sem", 0, RT_IPC_FLAG_PRIO);

2. rt_sem_init

用于静态信号量的初始化,一般用于静态分配内存的场景。

rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag);

参数说明:

  • sem:信号量对象的指针(需要预先定义好该信号量结构体)。

  • name:信号量的名字。

  • value:信号量的初始值。

  • flag:IPC 对象的属性标志(同 rt_sem_create)。

返回值:

  • 成功:返回 RT_EOK

  • 失败:返回相应的错误代码。

示例:

/* 静态信号量定义 */
static struct rt_semaphore static_sem;
/* 静态信号量初始化,初始值为1,用于互斥操作 */
rt_sem_init(&static_sem, "static_sem", 1, RT_IPC_FLAG_PRIO);

3. rt_sem_take

用于获取信号量,线程会尝试获取信号量,如果信号量的计数值为 0,线程会进入等待状态,直到信号量被释放或超时。

rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time);

参数说明:

  • sem:信号量的句柄。

  • time:超时时间(单位为系统 tick),表示最大等待时间。如果设置为 RT_WAITING_FOREVER,线程会一直等待。

返回值:

  • 成功:返回 RT_EOK

  • 失败:返回 -RT_ETIMEOUT(超时)或其他错误代码。

示例:

/* 线程1等待信号量,直到收到信号 */
rt_sem_take(sync_sem, RT_WAITING_FOREVER);

4. rt_sem_release

用于释放信号量,增加信号量的计数值,并唤醒等待该信号量的线程。

rt_err_t rt_sem_release(rt_sem_t sem);

参数说明:

  • sem:信号量的句柄。

返回值:

  • 成功:返回 RT_EOK

  • 失败:返回相应的错误代码。

示例:

/* 线程2完成任务后释放信号量,通知线程1 */
rt_sem_release(sync_sem);

5. rt_sem_delete

用于删除动态信号量(仅限于 rt_sem_create 创建的动态信号量)。

rt_err_t rt_sem_delete(rt_sem_t sem);

参数说明:

  • sem:信号量的句柄。

返回值:

  • 成功:返回 RT_EOK

  • 失败:返回相应的错误代码。

示例:

/* 删除动态创建的信号量 */
rt_sem_delete(sync_sem);
/*
 * Copyright (c) 2006-2025, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-02-06     RT-Thread    first version
 */

#include <rtthread.h> // 包含RT-Thread的核心头文件

#define DBG_TAG "main"    // 定义日志标签
#define DBG_LVL DBG_LOG   // 定义日志级别为普通日志
#include <signal.h>       // 包含POSIX信号头文件,用于信号处理
#include <rtdbg.h>        // 包含调试日志的头文件

static rt_thread_t thread1; // 定义线程1的句柄

// 线程1的信号处理函数
void thread_signal_handler(int signal)
{
    // 打印接收到的信号编号
    rt_kprintf("线程1进入信号处理函数处理任务,收到信号:%d...\n", signal);
    // signal.h文件中定义了SIGUSR1为30(用户定义信号1)
}

// 线程1的入口函数
void thread_entry1(void *parameter)
{
    // 安装信号处理函数
    rt_signal_install(SIGUSR1, thread_signal_handler);

    // 解除信号阻塞,使线程能够接收信号
    rt_signal_unmask(SIGUSR1);

    // 打印日志:线程1等待信号
    rt_kprintf("线程1等待信号...\n");

    // 无限循环
    while (1)
    {
        // 打印日志:线程1正在处理日常任务
        rt_kprintf("线程1正在处理日常任务...\n");

        // 线程延迟500毫秒
        rt_thread_mdelay(500);
    }
}

// 主函数
int main(void)
{
    // 创建线程1
    // 参数说明:
    // "thread1": 线程名称
    // thread_entry1: 线程入口函数
    // RT_NULL: 线程参数
    // 1024: 线程栈大小(单位:字节)
    // 10: 线程优先级(数值越小,优先级越高)
    // 10: 线程时间片
    thread1 = rt_thread_create("thread1", thread_entry1, RT_NULL, 1024, 10, 10);

    // 如果线程1创建成功,则启动线程1
    if (thread1 != RT_NULL)
    {
        rt_thread_startup(thread1);
    }

    // 主线程延迟2000毫秒
    rt_thread_mdelay(2000);

    // 向线程1发送SIGUSR1信号
    rt_thread_kill(thread1, SIGUSR1);

    // 主函数返回0,表示正常结束
    return 0;
}

实现效果: image-20250207184238465

遇到的问题:

如果不在内核中进行使能,则使用信号的相关函数会报错

image-20250207183417071