单个指定事件唤醒线程,任意事件唤醒线程,多个指定事件一块儿唤醒线程。信号量主要用于“一对一”的线程同步,当须要“一对多”、“多对一”、“多对多”的同步时,就须要事件集来处理了。RT-Thread中的事件集用一个32位无符号整型变量来表示,变量中的一个位表明一个事件,线程经过“逻辑与”或“逻辑或”与一个或多个事件创建关联造成一个事件组合。html
struct rt_event { struct rt_ipc_object parent; // 从ipc_object继承而来 rt_uint32_t set; // 事件集 set } typedef struct rt_event *rt_event_t; 静态事件集:struct rt_event static_evt; 动态事件集:rt_event_t dynamic_evt;
静态事件集操做 rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t falg) //上同 rt_err_t rt_event_detach(rt_event_t event)
动态事件集操做 rt_event_t rt_event_create(const char *name, rt_uint8_t flag) rt_err_t rt_event_delete(rt_event event)
// set的值为0x01则表明第0个事件发生了,0x08则表明第3个事件发生了 // 1 << 0, 1 << 3 rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t *recved) // set的值表示对哪一个事件感兴趣。例如0x01 | 0x08,则表示对第0个事件和第3个事件感兴趣 // option: RT_EVENT_FLAG_AND:都发生才唤醒 RT_EVENT_FLAG_OR:有一个发生就唤醒 RT_EVENT_FLAG_CLEAR:线程唤醒后,系统会将事件的set对应的位清零。不然系统不会清除对应位 // timeout: 上同 // recved: 保存接收到的事件set
邮箱用于线程间通讯,特色是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的4字节内容(针对32位处理系统,指针的大小即为4个字节,因此一封邮件刚好可以容纳一个指针)。线程或中断服务例程把一封4字节长度的邮件发送到邮箱中,而其余须要的线程能够从邮箱中接受这些邮件并进行处理
缓存
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; 静态邮箱:struct rt_mailbox static_mb; 动态邮箱:rt_mailbox_t dynamic_mb;
// 静态邮箱。flag: RT_IPC_FLAG_FIFO, RT_IPC_FLAG_PRIO // 若是size=10,则 msgpool就要有4 * 10 = 40byte的空间 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_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) rt_err_t rt_mbdelete(rt_mailbox_t mb)
// value就是邮件的内容,4字节。若是发送的内容<4字节,则直接赋值给value便可,若是不少,那么能够传送地址。 // 若是邮箱已经满了,那么会直接返回错误 // 能够在线程和中断中调用 rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value); // 若是邮箱满了,则最多等待timeout时间。 // 只能在线程中调用,由于它会形成阻塞 rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout)
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout)
消息队列是RT-Thread另外一种经常使用的线程间通讯方式,消息队列是对邮箱的扩展。消息队列可以接收来自线程或中断服务例程中发出的不固定长度的消息,并把消息缓存在本身的内存空间中,而其它线程可以从消息队列中读取相应的消息,并进行相应的处理。支持紧急消息发送,即将紧急消息连接到消息列表链表头。当消息队列满,还往消息队列发送消息,该发送就会失败。当消息队列空,还从消息队列接收消息,该接收就会失败。
ide
struct rt_messagequeue { struct rt_ipc_object parent; // 继承自IPC对象 void *msg_pool; // 指向消息队列空间的地址 rt_uint16_t msg_size; // 消息最大长度.在rtconfig.h 中定义对其字节,通常是4字节对齐。所以最小为4,应设置为4的倍数 rt_uint16_t max_msgs; // 消息队列容量,能容纳最多消息个数。例如设置msg_pool的大小为1024byte,那么max_msgs = 1024 / (msg_size + 4指针地址) = 1024/8 rt_uint16_t entry; // 消息队列中消息个数 void *msg_queue_head; // 消息队列头指针 void *msg_queue_tail; // 消息队列尾指针 void *msg_queue_free; // 消息队列未被使用的消息框 } typedef struct rt_messagequeue *rt_mq_t; 静态消息队列:struct rt_messagequeue static_mq 动态消息队列:rt_mq_t dynamic_mq
rt_err_t rt_mq_init(rt_mq_t mq, const char *name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size, rt_uint8_t flag) // RT_IPC_FLAG_FIFO, RT_IPC_FLAG_PRIO rt_err_t rt_mq_detach(rt_mq_t mq)
rt_mq_t rt_mq_create(const char *name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag) rt_err_t rt_mq_delete(rt_mq_t mq)
// size <= msg_size.将消息放在尾部 rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size) // 紧急消息,消息放在列表头部 rt_err_t rt_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size)
// timeout不等于0,则收不到消息就会挂起 rt_err_t rt_mq_recv(rt_mq_t mq, void *buffer, rt_size_t size, rt_int32_t timeout)
软件定时器是由操做系统提供的一类系统接口,构建在硬件定时器基础之上(系统滴答定时器),软件定时器使系统可以提供不受数目限制的定时器服务。RT-Thread提供的软件定时器,以系统节拍(OS Tick)的时间长度为定时单位,提供了基于系统节拍整数倍的定时能力,即定时器数值是OS Tick的整数倍。例如一个OS Tick是10ms,那么上层软件定时器只能提供10的倍数的定时。到时以后,会调用用户设置的回调函数函数
超时函数在中断上下文环境中执行。此模式在定时器初始化时指定。对于超时函数的要求和中断服务例程的要求相同。执行时间应该尽可能短,执行时不该致使当前上下文挂起,HARD_TIMER模式是RT-Thread软件定时器的默认方式ui
超时函数在系统timer线程的线程上下文中执行。经过宏定义RT_USING_TIMER_SOFT来决定是否启用该模式。当启动SOFTTIMER模式后,能够在定时器初始化时指定定时器工做在SOFTTIMER模式操作系统
struct rt_timer { struct rt_object parent; // 从系统对象继承而来 rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; // 系统定时器链表 void (*timeout_func)(void *parameter); // 超时回调函数 void *parameter; // 回调函数输入参数 rt_tick_t init_tick; // 指定超时的时钟节拍。例如60 * 10 = 600ms rt_tick_t timeout_tick; // 系统在超时时的系统节拍 }; typedef struct rt_timer *rt_timer_t; 静态软件定时器:struct rt_timer static_timer 动态软件定时器:rt_timer_t dynamic_timer
void rt_timer_init(rt_timer_t timer, const char *name, void(*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag); // RT_TIMER_FLAG_ONE_SHOT, RT_TIMER_FLAG_PERIODIC, RT_TIMER_FLAG_HARD_TIMER, RT_TIMER_FLAG_SOFT_TIMER 第1和第2个选一个 | 第3和第4个选一个 // 没有回调参数就传RT_NULL rt_err_t rt_timer_detach(rt_timer_t timer)
rt_timer_t rt_timer_create(const char *name, void(*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag) rt_err_t rt_timer_delete(rt_timer_t timer)
rt_err_t rt_timer_start(rt_timer_t timer)
rt_err_t rt_timer_stop(rt_timer_t timer)
动态内存堆能够分配任意大小的内存块,很是灵活和方便,但其存在明显的缺点:一是分配效率不高,在每次分配时,都要进行空闲内存块查找;二是容易产生内存碎片。为了提升内存分配效率,而且避免内存碎片,RT-Thread提供了另外一种内存管理方法:内存池。内存池是一种内存分配方式,用于分配大量大小相同的小内存块,使用内存池能够极大的加快内存分配与释放的速度,且能尽可能避免内存碎片化。RT-Thread的内存池支持线程挂起,当内存池无空闲内存块时,申请线程会被挂起,直到内存池中有新的可用内存块,再将挂起的线程唤醒。基于这个特色内存池很是适合须要经过内存资源进行同步的场景。
线程
struct rt_mempool { struct rt_object parent; // 从系统对象继承 void *start_address; // 内存池起始地址 rt_size_t size; // 内存池大小 rt_size_t block_size; // 内存池中一个内存块大小 rt_uint8_t *block_list; // 内存池中小内存块链表 rt_size_t block_total_count; // 记录内存池中能容纳多少小内存块 rt_size_t block_free_count; // 内存池中空闲内存块个数 rt_list_t suspend_thread; // 挂载在该资源上的线程 rt_size_t suspend_thread_count; // 挂载在该资源上的线程个数 }; typedef struct rt_mempool *rt_mp_t; 静态内存池:struct rt_mempool static_mp; 动态内存池:rt_mp_t dynamic_mp;
静态内存池 // block_size仍然要遵循字节对齐。例如4字节对齐,就必须设置成4的整数倍。有了block_size和size就能够计算内存块数量=size/(block_size + 4)。其中4为指针大小 rt_err_t rt_mp_init(struct rt_mempool *mp, const char *name, void *start, rt_size_t size, rt_size_t block_size) rt_err_t rt_mp_detach(struct rt_mempool *mp)
rt_mp_t rt_mp_create(const char *name, rt_size_t block_count, rt_size_t block_size) rt_err_t rt_mp_delete(rt_mp_t mp)
// time参数为0,则马上返回结果,若是time大于0,则无内存块会挂起线程,若是time小于0,则无内存块会一直挂起线程,直到有内存块 void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
// 输入内存块的地址 void rt_mp_free(void *block)
本文做者: CrazyCatJack指针
本文连接: https://www.cnblogs.com/CrazyCatJack/p/14408849.htmlcode
版权声明:本博客全部文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!视频
关注博主:若是您以为该文章对您有帮助,能够点击文章右下角推荐一下,您的支持将成为我最大的动力!