rtthread:消息队列
1 消息队列
消息队列是一种常用的线程间异步通讯方式;
消息队列能够接收来自线程或中断中不固定长度的消息,并把消息缓存在自己的内存空间中,供线程间进行异步通讯;
1.1 结构体定义
//rtconfig.h 源码默认注释掉未开启,用到消息队列的时候需要自己开启; #define RT_USING_MESSAGEQUEUE //rtdef.h #ifdef RT_USING_MESSAGEQUEUE struct rt_messagequeue { struct rt_ipc_object parent; /**< inherit from ipc_object */ void *msg_pool; /**< start address of message queue */ rt_uint16_t msg_size; /**< message size of each message */ rt_uint16_t max_msgs; /**< max number of messages */ rt_uint16_t entry; /**< index of messages in the queue */ void *msg_queue_head; /**< list head */ void *msg_queue_tail; /**< list tail */ void *msg_queue_free; /**< pointer indicated the free node of queue */ }; typedef struct rt_messagequeue *rt_mq_t; #endif //rtdef.h suspend_thread在create函数中初始化,作为suspend线程的链表节点来挂载; struct rt_ipc_object { struct rt_object parent; /**< inherit from rt_object */ rt_list_t suspend_thread; /**< threads pended on this resource */ }; //ipc.c 和rt_mq_init()函数一起定义的; //rt_mq_init()静态创建mq结构体,rt_mq_create()动态创建mq结构体;demo里用的动态创建的; #ifdef RT_USING_MESSAGEQUEUE struct rt_mq_message { struct rt_mq_message *next; };
1.2 rt_mq_create 消息队列初始化
分配rt_messagequeue结构体内存,初始化以及分配空闲链表消息池;
//ipc.c rt_mq_t rt_mq_create(const char *name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag) { struct rt_messagequeue *mq; struct rt_mq_message *head; register rt_base_t temp; RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ mq = (rt_mq_t)rt_object_allocate(RT_Object_Class_MessageQueue, name); if (mq == RT_NULL) return mq; /* set parent */ mq->parent.parent.flag = flag; /* init ipc object */ rt_ipc_object_init(&(mq->parent)); /* init message queue */ /* get correct message size */ mq->msg_size = RT_ALIGN(msg_size, RT_ALIGN_SIZE); mq->max_msgs = max_msgs; /* allocate message pool (消息size + messagenext链表)*消息个数 */ mq->msg_pool = RT_KERNEL_MALLOC((mq->msg_size + sizeof(struct rt_mq_message)) * mq->max_msgs); if (mq->msg_pool == RT_NULL) { rt_mq_delete(mq); return RT_NULL; } /* init message list */ mq->msg_queue_head = RT_NULL; mq->msg_queue_tail = RT_NULL; /* init message empty list */ mq->msg_queue_free = RT_NULL; for (temp = 0; temp < mq->max_msgs; temp ++) { head = (struct rt_mq_message *)((rt_uint8_t *)mq->msg_pool + temp * (mq->msg_size + sizeof(struct rt_mq_message))); head->next = mq->msg_queue_free; mq->msg_queue_free = head; } /* the initial entry is zero */ mq->entry = 0; return mq; } RTM_EXPORT(rt_mq_create);
1.3 rt_mq_send 消息队列发送
从空闲msg_pool中取出msg_queue_free链表,然后插入到消息msg_pool中等待发送;
如果mq->parent.suspend_thread不为空,则把suspend_thread从suspend_list中删除之后然后再挂载回优先级表中启动调度;
//ipc.c rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size) { register rt_ubase_t temp; struct rt_mq_message *msg; RT_ASSERT(mq != RT_NULL); RT_ASSERT(buffer != RT_NULL); RT_ASSERT(size != 0); /* greater than one message size */ if (size > mq->msg_size) return -RT_ERROR; RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mq->parent.parent))); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* get a free list, there must be an empty item */ msg = (struct rt_mq_message *)mq->msg_queue_free; //msg: &head2; /* message queue is full */ if (msg == RT_NULL) { /* enable interrupt */ rt_hw_interrupt_enable(temp); return -RT_EFULL; } /* move free list pointer */ mq->msg_queue_free = msg->next; //msg_queue_free: &head1; /* enable interrupt */ rt_hw_interrupt_enable(temp); /* the msg is the new tailer of list, the next shall be NULL */ msg->next = RT_NULL; //msg->next:null,不知道为什么要清空,因为作为一个新的消息等会用来插入msg_pool中; /* copy buffer */ rt_memcpy(msg + 1, buffer, size); //将消息存入msg_size大小的空间中,不知道为什么要加1,这里已经是新消息的地址啦; /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* link msg to message queue */ if (mq->msg_queue_tail != RT_NULL) { /* if the tail exists, */ ((struct rt_mq_message *)mq->msg_queue_tail)->next = msg; } /* set new tail */ mq->msg_queue_tail = msg; /* if the head is empty, set head */ if (mq->msg_queue_head == RT_NULL) mq->msg_queue_head = msg; /* increase message entry */ mq->entry ++; /* resume suspended thread */ if (!rt_list_isempty(&mq->parent.suspend_thread)) { rt_ipc_list_resume(&(mq->parent.suspend_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); rt_schedule(); return RT_EOK; } /* enable interrupt */ rt_hw_interrupt_enable(temp); return RT_EOK; } RTM_EXPORT(rt_mq_send);
1.4 rt_mq_recv消息队列接收
从消息队列中取出数据,然后把取出数据的消息地址放回空闲池里;
//ipc.c rt_err_t rt_mq_recv(rt_mq_t mq, void *buffer, rt_size_t size, rt_int32_t timeout) { struct rt_thread *thread; register rt_ubase_t temp; struct rt_mq_message *msg; rt_uint32_t tick_delta; RT_ASSERT(mq != RT_NULL); RT_ASSERT(buffer != RT_NULL); RT_ASSERT(size != 0); /* initialize delta tick */ tick_delta = 0; /* get current thread */ thread = rt_thread_self(); RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mq->parent.parent))); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* for non-blocking call 如果消息队列中没有消息且不等待消息,就直接返回;*/ if (mq->entry == 0 && timeout == 0) { rt_hw_interrupt_enable(temp); return -RT_ETIMEOUT; } /* message queue is empty 如果消息队列中没有消息,通过timeout判断是否等待处理;*/ while (mq->entry == 0) { RT_DEBUG_IN_THREAD_CONTEXT; /* reset error number in thread */ thread->error = RT_EOK; /* no waiting, return timeout */ if (timeout == 0) { /* enable interrupt */ rt_hw_interrupt_enable(temp); thread->error = -RT_ETIMEOUT; return -RT_ETIMEOUT; } /* suspend current thread */ rt_ipc_list_suspend(&(mq->parent.suspend_thread), thread, mq->parent.parent.flag); /* has waiting time, start thread timer */ if (timeout > 0) { /* get the start tick of timer */ tick_delta = rt_tick_get(); RT_DEBUG_LOG(RT_DEBUG_IPC, ("set thread:%s to timer list\n", thread->name)); /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &timeout); rt_timer_start(&(thread->thread_timer)); } /* enable interrupt */ rt_hw_interrupt_enable(temp); /* re-schedule */ rt_schedule(); /* recv message */ if (thread->error != RT_EOK) { /* return error */ return thread->error; } /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* if it's not waiting forever and then re-calculate timeout tick */ if (timeout > 0) { tick_delta = rt_tick_get() - tick_delta; timeout -= tick_delta; if (timeout < 0) timeout = 0; } } /* get message from queue **********先放着,**************************/ msg = (struct rt_mq_message *)mq->msg_queue_head; /* move message queue head */ mq->msg_queue_head = msg->next; /* reach queue tail, set to NULL */ if (mq->msg_queue_tail == msg) mq->msg_queue_tail = RT_NULL; /* decrease message entry */ mq->entry --; /* enable interrupt */ rt_hw_interrupt_enable(temp); /* copy message 发送函数中msg地址加1不理解,不过这里也+1回去了,只是觉得没必要;*/ rt_memcpy(buffer, msg + 1, size > mq->msg_size ? mq->msg_size : size); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* put message to free list 将用完的消息地址放回空闲池;存放方式类似堆栈push和pop; */ msg->next = (struct rt_mq_message *)mq->msg_queue_free; mq->msg_queue_free = msg; /* enable interrupt */ rt_hw_interrupt_enable(temp); RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mq->parent.parent))); return RT_EOK; } RTM_EXPORT(rt_mq_recv);
1.5 收发消息队列图
不知道为什么用memcpy( )的时候收发的msg 地址都加1了,我觉得那里分配的地址应该不需要加1的,可是不加1会报错,先放着吧,以后再说;
1.6 suspend_thread链表节点
//在rt_mq_send函数中,发送数据的时候,储存完数据之后会把suspend_thread恢复; //这个函数的目的是要是先执行了接收函数,而消息队列中又没有消息,接收函数就被挂起了,这里接收到消息了,所以恢复函数; /* resume suspended thread */ if (!rt_list_isempty(&mq->parent.suspend_thread)) { rt_ipc_list_resume(&(mq->parent.suspend_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); rt_schedule(); return RT_EOK; } //在rt_mq_recv函数中,消息池中需要先存储了数据才可以接收数据;要是没有存储数据就先把线程挂起,直到while(!entry); while (mq->entry == 0) { /* suspend current thread */ rt_ipc_list_suspend(&(mq->parent.suspend_thread), thread, mq->parent.parent.flag); //要是定了超时时间,那么就开启定时器,时间到了就回来看看是否退出while循坏; } //这个信息是如何在两个线程间传递的,我有一点不理解,先放着吧; //消息队列就相当于静态内存,然后存取数据的时候传递的是地址来存取数据;
2 小结
每次分配地址和数据的时候分界点的边界总是有点迷糊,都得重新找一遍规律,这样不行呀;
指针向下移动第几个,就是移动到第几位地址;[0]+5 = [5],[2]+5 = [7];msg_pool的地址从head头移动到head尾,要+1才到下面消息地址;
接收里有好几个if(timeout>0),也不知道都有啥用;开启定时器,定时器到期之后再回来执行,看看是不是要退出while循坏;
parent.suspend_thread的处理流程是怎么样的呢?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?