微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Rt-Thread邮箱源码分析

1、邮箱机制

可以将邮箱看做一个环形队列。放入邮件的操作等同于入队。取出邮件的操作等同于出队。entry是队列条目大小,in_offset是队头,out_offset是队尾,size是队列大小。

注:邮箱中的每一封邮件只能容纳固定的指针大小的内容

2、邮箱控制块

邮箱控制块是用于管理邮箱的一个数据结构,由结构体 struct rt_mailBox表示。

struct rt_mailBox
{
    struct rt_ipc_object parent;                        /* ipc对象 */

    rt_uint32_t         *msg_pool;                      /* 邮箱缓冲区的起始地址 */

    rt_uint16_t          size;                          /* 邮箱缓冲区的大小 */

    rt_uint16_t          entry;                         /* 邮箱中邮件的数目 */
    rt_uint16_t          in_offset;                     /* 邮箱缓冲的尾指针 */
    rt_uint16_t          out_offset;                    /* 邮箱缓冲的头指针 */

    rt_list_t            suspend_sender_thread;         /* 因等待发送邮件而挂起的线程链表 */
};
typedef struct rt_mailBox *rt_mailBox_t;

3、rt_mb_init()函数

函数将初始化邮箱,并将其交由对象容器管理。

rt_err_t rt_mb_init(rt_mailBox_t mb,
                    const char  *name,
                    void        *msgpool,
                    rt_size_t    size,
                    rt_uint8_t   flag)
{
    RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL

    rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name); //初始化邮箱对象

    mb->parent.parent.flag = flag; //设置邮箱标志

    rt_ipc_object_init(&(mb->parent)); //初始化邮箱ipc对象

    /* 初始化邮箱 */
    mb->msg_pool   = msgpool; //设置邮箱msg_pool为msgpool
    mb->size       = size; //设置邮箱size为size
    mb->entry      = 0; //设置邮箱entry为0
    mb->in_offset  = 0; //设置邮箱in_offset为0
    mb->out_offset = 0; //设置邮箱out_offset为0

    rt_list_init(&(mb->suspend_sender_thread)); //初始化邮箱发送挂起线程链表

    return RT_EOK; //返回RT_EOK
}

4、rt_mb_create()函数

函数将从内存中分配邮箱对象。

rt_mailBox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)
{
    rt_mailBox_t mb;

    RT_DEBUG_NOT_IN_INTERRUPT; //断言不在ISR中调用

    mb = (rt_mailBox_t)rt_object_allocate(RT_Object_Class_MailBox, name); //分配邮箱对象
    if (mb == RT_NULL) //mb等于RT_NULL
        return mb; //返回RT_NULL

    mb->parent.parent.flag = flag; //设置邮箱标志

    rt_ipc_object_init(&(mb->parent)); //初始化邮箱ipc对象

    /* 初始化邮箱 */
    mb->size     = size; //设置邮箱的size为size
    mb->msg_pool = RT_KERNEL_MALLOC(mb->size * sizeof(rt_uint32_t)); //分配内存
    if (mb->msg_pool == RT_NULL) //分配内存失败
    {
        rt_object_delete(&(mb->parent.parent)); //删除邮箱对象

        return RT_NULL; //返回RT_NULL
    }
    mb->entry      = 0; //设置邮箱的entry为0
    mb->in_offset  = 0; //设置邮箱的in_offset为0
    mb->out_offset = 0; //设置邮箱的out_offset为0

    rt_list_init(&(mb->suspend_sender_thread)); //初始化邮箱发送挂起线程链表

    return mb;
}

5、rt_mb_detach()函数

函数将将邮箱与资源管理分离。

rt_err_t rt_mb_detach(rt_mailBox_t mb)
{
    /* 参数检查 */
    RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
    RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象是邮箱
    RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent)); //断言邮箱是静态的

    rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); //唤醒因获取邮箱资源而挂起的所有线程
    rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); //唤醒邮箱发送挂起线程链表上的所有线程

    rt_object_detach(&(mb->parent.parent)); //脱离邮箱对象

    return RT_EOK; //返回RT_EOK
}

6、rt_mb_delete()函数

函数删除邮箱对象并释放内存。

rt_err_t rt_mb_delete(rt_mailBox_t mb)
{
    RT_DEBUG_NOT_IN_INTERRUPT; //断言不在ISR中调用

    /* 参数检查 */
    RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
    RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象为邮箱对象
    RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent) == RT_FALSE); //断言邮箱不是静态的
    rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); //唤醒因获取邮箱资源而挂起的所有线程

    rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); //唤醒邮箱发送挂起线程链表上的所有线程

    RT_KERNEL_FREE(mb->msg_pool); //释放邮箱资源

    rt_object_delete(&(mb->parent.parent)); //删除邮箱对象

    return RT_EOK; //返回RT_EOK
}

7、rt_mb_send_wait()函数

函数将向邮箱对象发送一封邮件如果邮箱已满,则当前线程将挂起直到超时。

rt_err_t rt_mb_send_wait(rt_mailBox_t mb,
                         rt_uint32_t  value,
                         rt_int32_t   timeout)
{
	struct rt_thread *thread;
	register rt_ubase_t temp;
	rt_uint32_t tick_delta;

	/* 参数检查 */
	RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
	RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象是邮箱

	tick_delta = 0; //将tick_delta设置为0

	thread = rt_thread_self(); //获取当前线程

	RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent))); //调用回调

	temp = rt_hw_interrupt_disable(); //关中断

	/* 对于非阻塞调用 */
	if (mb->entry == mb->size && timeout == 0) //邮箱满了
	{
		rt_hw_interrupt_enable(temp); //开中断

		return -RT_EFULL; //返回-RT_EFULL
	}

	while (mb->entry == mb->size) //邮箱满了
	{
		thread->error = RT_EOK; //将线程的错误码设置为RT_EOK

		if (timeout == 0) //等待超时
		{
			rt_hw_interrupt_enable(temp); //开中断

			return -RT_EFULL; //返回-RT_EFULL
		}

		RT_DEBUG_IN_THREAD_CONTEXT; //上下文检查

		rt_ipc_list_suspend(&(mb->suspend_sender_thread),
							thread,
							mb->parent.parent.flag); //挂起当前线程

		/* 需要等待,启动线程定时器 */
		if (timeout > 0) 
		{
			tick_delta = rt_tick_get(); //获取当前时间

			RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_send_wait: start timer of thread:%s\n",
										thread->name)); //打印信息

			rt_timer_control(&(thread->thread_timer),
							RT_TIMER_CTRL_SET_TIME,
							&timeout); //设置线程定时器超时时间
			rt_timer_start(&(thread->thread_timer)); //启动线程定时器
		}

		rt_hw_interrupt_enable(temp); //开中断

		rt_schedule(); //调度

		if (thread->error != RT_EOK) //线程的错误码不等于RT_EOK
		{
			return thread->error; //返回错误码
		}

		temp = rt_hw_interrupt_disable(); //关中断

		/* 重新计算超时时间 */
		if (timeout > 0)
		{
			tick_delta = rt_tick_get() - tick_delta; //计算从设置线程定时器起到现在经过的时间
			timeout -= tick_delta; //计算剩余的超时时间
			if (timeout < 0)
				timeout = 0;  //如果超时时间小于0,设置超时时间等于0
		}
	}

	/* 队列操作,入队 */
	mb->msg_pool[mb->in_offset] = value; //放入邮件
	++ mb->in_offset; //邮箱in_offset加1
	if (mb->in_offset >= mb->size) //邮箱in_offset大于size
		mb->in_offset = 0; //设置邮箱in_offset为0
	mb->entry ++; //邮箱条目加1

	/* 唤醒挂起线程 */
	if (!rt_list_isempty(&mb->parent.suspend_thread)) //邮箱挂起线程链表非空
	{
		rt_ipc_list_resume(&(mb->parent.suspend_thread)); //唤醒挂起线程

		rt_hw_interrupt_enable(temp); //开中断

		rt_schedule(); //调度

		return RT_EOK; //返回RT_EOK
	}

	rt_hw_interrupt_enable(temp); //开中断

	return RT_EOK; //返回RT_EOK
}

8、rt_mb_send()函数

函数将向邮箱发送一封邮件(非阻塞)。

rt_err_t rt_mb_send(rt_mailBox_t mb, rt_uint32_t value)
{
	return rt_mb_send_wait(mb, value, 0); //超时参数为0
}

9、rt_mb_recv()函数

函数将接收来自邮箱的邮件,如果邮箱对象中没有邮件,线程将等待指定的时间。

rt_err_t rt_mb_recv(rt_mailBox_t mb, rt_uint32_t *value, rt_int32_t timeout)
{
	struct rt_thread *thread;
	register rt_ubase_t temp;
	rt_uint32_t tick_delta;

	/* 参数检查 */
	RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
	RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象是邮箱

	tick_delta = 0; //设置tick_delta等于0

	thread = rt_thread_self(); //获取当前线程

	RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mb->parent.parent))); //调用回调

	temp = rt_hw_interrupt_disable(); //关中断

	/* 对于非阻塞调用 */
	if (mb->entry == 0 && timeout == 0) //邮箱为空
	{
		rt_hw_interrupt_enable(temp); //开中断

		return -RT_ETIMEOUT; //返回-RT_ETIMEOUT
	}

	while (mb->entry == 0) //邮箱为空
	{
		thread->error = RT_EOK; //设置线程错误码为RT_EOK

		if (timeout == 0) //等待超时
		{
			rt_hw_interrupt_enable(temp); //开中断

			thread->error = -RT_ETIMEOUT; //设置线程错误码为-RT_ETIMEOUT

			return -RT_ETIMEOUT; //返回-RT_ETIMEOUT
		}

		RT_DEBUG_IN_THREAD_CONTEXT; //上下文检查

		rt_ipc_list_suspend(&(mb->parent.suspend_thread),
							thread,
							mb->parent.parent.flag); //挂起当前线程

		/* 需等待,开启线程定时器 */
		if (timeout > 0)
		{
			tick_delta = rt_tick_get(); //获取当前时间

			RT_DEBUG_LOG(RT_DEBUG_IPC, ("mb_recv: start timer of thread:%s\n",
										thread->name)); //打印信息

			rt_timer_control(&(thread->thread_timer),
							RT_TIMER_CTRL_SET_TIME,
							&timeout); //设置线程定时器超时时间
			rt_timer_start(&(thread->thread_timer)); //启动线程定时器
		}

		rt_hw_interrupt_enable(temp); //开中断

		rt_schedule(); //调度

		if (thread->error != RT_EOK) //线程错误码不等于RT_EOK
		{
			return thread->error; //返回线程错误码
		}

		temp = rt_hw_interrupt_disable(); //关中断

		/* 重新计算超时时间 */
		if (timeout > 0)
		{
			tick_delta = rt_tick_get() - tick_delta; //计算从设置线程定时器起到现在经过的时间
			timeout -= tick_delta; //计算剩余超时时间

			if (timeout < 0) //如果超时时间小于0
				timeout = 0; //设置超时时间为0
		}
	}

	/* 队列操作,出队 */
	*value = mb->msg_pool[mb->out_offset]; //取出邮件
	++ mb->out_offset; //设置邮箱out_offset加1
	if (mb->out_offset >= mb->size) //设置邮箱out_offset大于mb->size
		mb->out_offset = 0; //设置邮箱out_offset为0
	mb->entry --; //邮箱条目减1

	/* 唤醒挂起线程 */
	if (!rt_list_isempty(&(mb->suspend_sender_thread))) //邮箱发送挂起线程非空
	{
		rt_ipc_list_resume(&(mb->suspend_sender_thread)); //唤醒邮箱发送挂起线程

		rt_hw_interrupt_enable(temp); //开中断

		RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); //调用回调函数

		rt_schedule(); //调度

		return RT_EOK; //返回RT_EOK
	}

	rt_hw_interrupt_enable(temp); //开中断

	RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mb->parent.parent))); //调用回调函数

	return RT_EOK; //返回RT_EOK
}

10、rt_mb_control()函数

rt_err_t rt_mb_control(rt_mailBox_t mb, int cmd, void *arg)
{
	rt_ubase_t level;

	/* 参数检查 */
	RT_ASSERT(mb != RT_NULL); //断言mb != RT_NULL
	RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); //断言对象为邮箱

	if (cmd == RT_IPC_CMD_RESET)
	{
		level = rt_hw_interrupt_disable(); //关中断

		rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); //唤醒因获取邮件而等待的所有线程
		rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); //唤醒因发送邮件而等待的所有线程

		/* 初始化邮箱 */
		mb->entry      = 0; //设置邮箱entry为0
		mb->in_offset  = 0; //设置邮箱in_offset为0
		mb->out_offset = 0; //设置邮箱out_offset为0

		rt_hw_interrupt_enable(level); //开中断

		rt_schedule(); //调度

		return RT_EOK; //返回RT_EOK
	}

	return -RT_ERROR; //返回-RT_ERROR
}

原文地址:https://www.jb51.cc/wenti/3281297.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐