本文是做为Zephyr内核对–数据传递对象简介的增补篇对所有的数据传进行效能分析。
每种数据传递对象的分析可以参考下面文章
Zephyr内核对象-数据传递之FIFO/LIFO
Zephyr内核对象-数据传递之Stack
Zephyr内核对象-数据传递之Message Queue
Zephyr内核对象-数据传递之邮箱
Zephyr内核对象-数据传递之管道
加锁方式
在数据传递过程中,内存有自旋锁和锁调度两种加锁方式:
自旋锁
本文只考虑单核CPU,在此情况下自旋锁退化为锁中断,我们假定没有配置非延迟中断,因此k_spin_lock将会锁所有的中断。
对应于k_spin_lock,可以使用k_spin_unlock,z_reschedule,z_pend_curr进行解锁
锁调度
k_sched_lock上锁后,不会再发生线程调度(但会响应中断),k_sched_unlock后恢复调度
数据传递方式分析和加锁影响
下面列出不同数据对象传递数据的方式,并列出传递时加锁的方式
queue/fifo/lifo
将数据的内存指针以链表形式管理,指针长度的数据copy
queue_insert
k_spin_lock->链表插入->z_reschedulez_impl_k_queue_get
k_spin_lock -> 从链表移除 ->k_spin_unlock
k_spin_lock -> z_pend_curr(无数据等待)
queue/fifo/lifo在传递数据的时候会锁中断,但由于只是链表操作,数据也只有指针长度,对系统效能影响不大。传递的是数据指针,数据0拷贝。
statck
将数据指针存放在堆栈中,指针长度的数据copy
- z_impl_k_stack_push
k_spin_lock -> 数据指针入栈 -> z_reschedule
k_spin_lock -> k_spin_unlock(堆栈满)
- z_impl_k_stack_pop
k_spin_lock->数据指针出堆栈->k_spin_unlock
k_spin_lock -> z_pend_curr(堆栈空,等数据)
在传递数据的时候会锁中断,但由于只是堆栈操作,单位数据也只有指针长度,对系统效能影响不大。传递的是数据指针,数据0拷贝。
消息列队
将消息以ring-buffer形式管理,一次传送copy的大小为消息的长度,消息可以包含所有数据,也可以只包含指向消息数据的指针,传递只copy消息,不copy消息数据指针指向的数据
z_impl_k_msgq_put
k_spin_lock -> copy消息大小->z_reschedule
k_spin_lock-> z_pend_curr (等待发送)
k_spin_lock-> k_spin_unlock(不等待发送)
z_impl_k_msgq_get
k_spin_lock->copy消息大小->z_reschedule
k_spin_lock-> z_pend_curr (等待数据)
k_spin_lock-> k_spin_unlock(不等待数据)
在传递数据的时候会锁中断,会对消息数据进行拷贝,为了减少对系统的影响,定义消息列队的消息时需要控制消息的长度,如果有大数据需要传递,只在消息里面传递数据的指针,传递数据0拷贝。
邮箱
将消息中包含消息数据指针,接收者将copy消息中数据指针指向所有数据,接收完后发送者才会退出。
mbox_message_put
k_spin_lock -> 匹配 ->等待接收copy数据z_pend_curr
k_spin_lock -> z_pend_curr(未匹配,等待发送)
k_spin_lock -> k_spin_unlock(未匹配,不等待发送)
k_mbox_get
k_spin_lock->匹配->k_spin_unlock->copy消息数据->通知copy完成
k_spin_lock-> k_spin_unlock(未匹配,等待接收消息)
k_spin_lock-> k_spin_unlock(未匹配,不等待)
只在匹配时会锁中断,对系统效能影响不大。在传递数据的时必须进行数据拷贝,数据过大需要考虑使用消息队列做0拷贝动作
管道
要发送的所有数据都在管道内,接收者会copy所有管道内的数据
z_pipe_put_internal
k_spin_lock -> 有thread等待读pipe数据->z_sched_lock->k_spin_unlock->copy pipe数据->k_sched_unlock
k_spin_lock -> 无thread等待读pipe数据->k_spin_unlock(不等待发送)
k_spin_lock -> 无thread等待读pipe数据->z_sched_lock->k_spin_unlock->z_sched_unlock_no_reschedule->k_spin_unlock(等待发送)
z_impl_k_pipe_get
k_spin_lock -> 有thread等待写pipe数据->z_sched_lock->k_spin_unlock->copy pipe数据->k_sched_unlock
k_spin_lock -> 无thread等待读pipe数据->k_spin_unlock(不等待接收)
k_spin_lock -> 无thread等待读pipe数据->z_sched_lock->k_spin_unlock->z_sched_unlock_no_reschedule->k_spin_unlock(等待接收)
只在检查pipe时锁中断,对系统效能影响不大,但在传递数据时需要锁调度,且是拷贝所有数据,在拷贝数据期间其它线程均无法打断,如果数据过大会影响系统效能。