RT-Thread 内核学习 >> (十三)邮箱的使用

邮箱工作机制

RT-Thread 操作系统的邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的4字节内容(针对32位处理器系统,指针大小即为4个字节,所以一封邮件恰好能够容纳一个指针)。

线程或ISP(中断服务程序)把一封4字节长度的邮件发送到邮箱中,而其他需要的线程可以从邮箱中接收这些邮件并进行处理。
RT-Thread 内核学习 >> (十三)邮箱的使用_第1张图片

邮箱控制块

struct rt_mailbox
{
    struct rt_ipc_object parent;                        /**< inherit from ipc_object */

    rt_ubase_t          *msg_pool;                      /**< start address of message buffer */

    rt_uint16_t          size;                          /**< size of message pool */

    rt_uint16_t          entry;                         /**< index of messages in msg_pool */
    rt_uint16_t          in_offset;                     /**< input offset of the message buffer */
    rt_uint16_t          out_offset;                    /**< output offset of the message buffer */

    rt_list_t            suspend_sender_thread;         /**< sender thread suspended on this mailbox */
};
typedef struct rt_mailbox *rt_mailbox_t;

rt_mailbox_t 是一个rt_mailbox 型的指针

成员 说明
parent 从系统IPC 对象继承
msg_pool 邮箱中消息的缓存区起始地址
size 邮箱大小
entry 邮箱中邮件个数的指示
in_offset 邮箱缓存区的进偏移量
out_offset 邮箱缓存区的出偏移量
suspend_sender_thread 挂起在邮箱上的发送线程(当邮箱已满,又想发送邮件的线程)

定义静态邮箱

(定义一个结构体)

struct rt_mailbox static_mb

定义动态邮箱

(定义一个结构体指针)

rt_mailbox_t dynamic_mb

邮箱的操作

初始化与脱离

邮箱的初始化与脱离是针对静态邮箱的。

rt_err_t rt_mb_init(rt_mailbox_t mb,
                    const char  *name,
                    void        *msgpool,
                    rt_size_t    size,
                    rt_uint8_t   flag);
                    
rt_err_t rt_mb_detach(rt_mailbox_t mb);

rt_mb_init

参数 说明
mb 邮箱控制块指针
name 邮箱名称
msgpool 邮箱缓存区地址
size 邮箱大小
flag 等候线程的排队方式(RT_IPC_FLAG_FIFO、RT_IPC_FLAG_PRIO)

调用邮箱初始化或脱离API 就会分别将邮箱添加进系统管理器中或将邮箱从系统管理器中脱离。

创建与删除

邮箱的创建与删除是针对动态邮箱的。

rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag);

rt_err_t rt_mb_delete(rt_mailbox_t mb);

rt_mb_create

可见该API 相比静态邮箱的初始化API 缺少了mb、msgpool 参数,因为动态邮箱是根据邮箱大小在系统中申请一段动态内存,创建成功返回一个邮箱指针

参数 说明
name 邮箱名称
size 邮箱大小
flag 等候线程的排队方式(RT_IPC_FLAG_FIFO、RT_IPC_FLAG_PRIO)

调用邮箱创建与删除API 就会分别创建邮箱进系统管理器中或将邮箱从系统管理器中移除。

发送邮件

rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value);

rt_err_t rt_mb_send_wait(rt_mailbox_t mb,
                         rt_ubase_t  value,
                         rt_int32_t   timeout);

rt_mb_send

该函数本质上就是参数timeout 为零的rt_mb_send_wait 函数

参数 说明
mb 邮箱指针
value 要发送的邮件内容

value 可以直接是要发送的邮件内容,也可以是邮件地址。

若邮箱已满,调用此函数将返回错误标志-RT_EFULL

rt_mb_send_wait

参数 说明
mb 邮箱指针
value 邮件内容
timeout 超时时间

value 可以直接是要发送的邮件内容,也可以是邮件地址。

若邮箱已满,则会根据timeout 进行相应时间等待。


rt_mb_send 函数(即timeout = 0的 rt_mb_send_wait 函数)可以在线程和中断中调用,当timeout 不为零时,rt_mb_send_wait 函数只能在线程中调用。

接收邮件

rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout);
参数 说明
mb 邮箱地址
value 存储邮件的内存地址(注意:此处需要传入指针的取地址,见例程)
timeout 等待超时时间(当邮箱为空时)

邮箱使用示例

#define THREAD_PRIORITY      10
#define THREAD_TIMESLICE     5

/* 邮箱控制块 */
static struct rt_mailbox mb;
/* 用于放邮件的内存池 */
static char mb_pool[128];

static char mb_str1[] = "I'm a mail!";
static char mb_str2[] = "this is another mail!";
static char mb_str3[] = "over";

ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;

/* 线程1入口 */
static void thread1_entry(void *parameter)
{
    char *str;

    while (1)
    {
        rt_kprintf("thread1: try to recv a mail\n");

        /* 从邮箱中收取邮件 */
        if (rt_mb_recv(&mb, (rt_uint32_t *)&str, RT_WAITING_FOREVER) == RT_EOK)
        {
            rt_kprintf("thread1: get a mail from mailbox, the content:%s\n", str);
            if (str == mb_str3)
                break;

            /* 延时100ms */
            rt_thread_mdelay(100);
        }
    }
    /* 执行邮箱对象脱离 */
    rt_mb_detach(&mb);
}

ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;

/* 线程2入口 */
static void thread2_entry(void *parameter)
{
    rt_uint8_t count;

    count = 0;
    while (count < 10)
    {
        count ++;
        if (count & 0x1)
        {
            /* 发送mb_str1地址到邮箱中 */
            rt_mb_send(&mb, (rt_uint32_t)&mb_str1);
        }
        else
        {
            /* 发送mb_str2地址到邮箱中 */
            rt_mb_send(&mb, (rt_uint32_t)&mb_str2);
        }

        /* 延时200ms */
        rt_thread_mdelay(200);
    }

    /* 发送邮件告诉线程1,线程2已经运行结束 */
    rt_mb_send(&mb, (rt_uint32_t)&mb_str3);
}

int mailbox_sample(void)
{
    rt_err_t result;

    /* 初始化一个mailbox */
    result = rt_mb_init(&mb,
                        "mbt",                      /* 名称是mbt */
                        &mb_pool[0],                /* 邮箱用到的内存池是mb_pool */
                        sizeof(mb_pool) / 4,        /* 邮箱中的邮件数目,因为一封邮件占4字节 */
                        RT_IPC_FLAG_FIFO);          /* 采用FIFO方式进行线程等待 */
    if (result != RT_EOK)
    {
        rt_kprintf("init mailbox failed.\n");
        return -1;
    }

    rt_thread_init(&thread1,
                   "thread1",
                   thread1_entry,
                   RT_NULL,
                   &thread1_stack[0],
                   sizeof(thread1_stack),
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread1);

    rt_thread_init(&thread2,
                   "thread2",
                   thread2_entry,
                   RT_NULL,
                   &thread2_stack[0],
                   sizeof(thread2_stack),
                   THREAD_PRIORITY, THREAD_TIMESLICE);
    rt_thread_startup(&thread2);
    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(mailbox_sample, mailbox sample);

你可能感兴趣的:(C,RT-Thread,嵌入式系统)