完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
一、IPC对象
再回顾下裸眼对象的派生和继承关系: 1.1 IPC对象控制块 前面已经介绍过直接继承自基对象rt_object的定时器对象rt_timer、内存池对象rt_mempool、线程对象rt_thread,下面要线程对象的介绍的同步与通信,线程间同步对象rt_sem / rt_mutex / rt_event和线程间通信对象rt_mb / rt_mq都直接继承自rt_ipc_object,而IPC对象又继承自基对象rt_object,在介绍线程间同步与通信对象前先介绍派生IPC对象rt_ipc_object,其数据结构如下: // rt-thread-4.0.1includertdef.h /** * Base structure of IPC object */ struct rt_ipc_object { struct rt_object parent; /**《 inherit from rt_object */ rt_list_t suspend_thread; /**《 threads pended on this resource */ }; /** * IPC flags and control command definitions */ #define RT_IPC_FLAG_FIFO 0x00 /**《 FIFOed IPC. @ref IPC. */ #define RT_IPC_FLAG_PRIO 0x01 /**《 PRIOed IPC. @ref IPC. */ #define RT_IPC_CMD_UNKNOWN 0x00 /**《 unknown IPC command */ #define RT_IPC_CMD_RESET 0x01 /**《 reset IPC object */ #define RT_WAITING_FOREVER -1 /**《 Block forever until get resource. */ #define RT_WAITING_NO 0 /**《 Non-block. */ [tr]flag位01备注[/tr] 位0RT_IPC_FLAG_FIFO:更多消息引导先出的处理方式。RT_IPC_FLAG_PRIO:按线程优先级的方式处理,即哪个线程的优先级高则哪个先操作IPC处理方式 rt_ipc_object继承自基对象rt_object的另外几个成员根据具体对象类型取值有所不同,等介绍具体对象类型时再介绍。 rt_ipc_object唯一的私有对象rt_ipc_object.suspend_thread是挂起等待线程链表节点,这些挂起等待线程共同构成了事件等待链表,事件挂起链表的组织顺序跟前面介绍的标志有关,如果设置了RT_IPC_FLAG_FIFO则挂起线程链表按先入先出排序,如果设置了RT_IPC_FLAG_PRIO则挂起线程按优先级排序。 1.2 IPC对象函数 派生IPC对象接口由于有一个函数派生IPC对象接口,因为有一个合法的成员函数挂起线程,对其的操作有链表初始化、链表节点插入/移除等,IPC对象的初始化函数与挂起线程函数实现代码如下: // rt-thread-4.0.1srcipc.c /** * This function will initialize an IPC object * * @param ipc the IPC object * * @return the operation status, RT_EOK on successful */ rt_inline rt_err_t rt_ipc_object_init(struct rt_ipc_object *ipc) { /* init ipc object */ rt_list_init(&(ipc-》suspend_thread)); return RT_EOK; } /** * This function will suspend a thread to a specified list. IPC object or some * double-queue object (mailbox etc.) contains this kind of list. * * @param list the IPC suspended thread list * @param thread the thread object to be suspended * @param flag the IPC object flag, * which shall be RT_IPC_FLAG_FIFO/RT_IPC_FLAG_PRIO. * * @return the operation status, RT_EOK on successful */ rt_inline rt_err_t rt_ipc_list_suspend(rt_list_t *list, struct rt_thread *thread, rt_uint8_t flag) { /* suspend thread */ rt_thread_suspend(thread); switch (flag) { case RT_IPC_FLAG_FIFO: rt_list_insert_before(list, &(thread-》tlist)); break; case RT_IPC_FLAG_PRIO: { struct rt_list_node *n; struct rt_thread *sthread; /* find a suitable position */ for (n = list-》next; n != list; n = n-》next) { sthread = rt_list_entry(n, struct rt_thread, tlist); /* find out */ if (thread-》current_priority 《 sthread-》current_priority) { /* insert this thread before the sthread */ rt_list_insert_before(&(sthread-》tlist), &(thread-》tlist)); break; } } /* * not found a suitable position, * append to the end of suspend_thread list */ if (n == list) rt_list_insert_before(list, &(thread-》tlist)); } break; } return RT_EOK; } 函数rt_ipc_list_suspend有一个参数标志表示前面介绍的IPC处理方式,RT_IPC_FLAG_FIFO比较简单快速,RT_IPC_FLAG_PRIO能实现更好的实时性,用户根据需求选择颜色的标志值。 有挂自然起床,考虑到IPC对象可能会被删除或退出,此时挂起在该IPC对象上的所有线程都需要被唤醒,因此还提供了唤醒全部挂线程的功能,实现代码如下: // rt-thread-4.0.1srcipc.c /** * This function will resume the first thread in the list of a IPC object: * - remove the thread from suspend queue of IPC object * - put the thread into system ready queue * * @param list the thread list * * @return the operation status, RT_EOK on successful */ rt_inline rt_err_t rt_ipc_list_resume(rt_list_t *list) { struct rt_thread *thread; /* get thread entry */ thread = rt_list_entry(list-》next, struct rt_thread, tlist); RT_DEBUG_LOG(RT_DEBUG_IPC, (“resume thread:%sn”, thread-》name)); /* resume it */ rt_thread_resume(thread); return RT_EOK; } /** * This function will resume all suspended threads in a list, including * suspend list of IPC object and private list of mailbox etc. * * @param list of the threads to resume * * @return the operation status, RT_EOK on successful */ rt_inline rt_err_t rt_ipc_list_resume_all(rt_list_t *list) { struct rt_thread *thread; register rt_ubase_t temp; /* wakeup all suspend threads */ while (!rt_list_isempty(list)) { /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* get next suspend thread */ thread = rt_list_entry(list-》next, struct rt_thread, tlist); /* set error code to RT_ERROR */ thread-》error = -RT_ERROR; /* * resume thread * In rt_thread_resume function, it will remove current thread from * suspend list */ rt_thread_resume(thread); /* enable interrupt */ rt_hw_interrupt_enable(temp); } return RT_EOK; } 二、线程同步管理 同步是指派对象的事件对象进行运行,线程同步是指多个线程通过特定的机制(如互斥量,事件对象,实际区)来控制线程之间的执行顺序,也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间将是无序的。 线程的同步方式有很多种,其核心思想都是:在访问临界区的进入/退出社区区的有很多种: 调用 rt_hw_interrupt_disable() 进入临界区,调用 rt_hw_interrupt_enable() 临界退出区; 调用 rt_enter_critical() 进入临界区,调用 rt_exit_critical() 退出临界区 2.1量信号管理对象 信号量的英文一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。 信号量工作示意图如下图所示,每个信号量对象有一个量值和一个线程等待数据,信号量对象的实例量、资源量量检测信号值为5个,则共有5个可以信号量实例(资源)被使用,当信号量实例发生时,再申请该信号的实例就会被启动在该信号量的等待量上,等待的信号实例()资源。 信号量控制块 在 RT-Thread 中,信号量控制块是用于管理信号量的一个数据结构,由体结构 rt_semaphore 表示。另一种 C 表达方式 rt_sem_t,表示是信号量的句柄,在 C 语言中的实现是指针信号量控制块的轨迹。信号量控制块结构的详细定义如下: // rt-thread-4.0.1includertdef.h /** * Semaphore structure */ struct rt_semaphore { struct rt_ipc_object parent; /**《 inherit from ipc_object */ rt_uint16_t value; /**《 value of semaphore. */ rt_uint16_t reserved; /**《 reserved field */ }; typedef struct rt_semaphore *rt_sem_t; rt_semaphore 对象从 rt_ipc_object 中派生,rt_semaphore.parent.parent.type 是RT_Object_Class_Semaphore,rtsemaphore.parent.parent.list 为已初始化/创建量信号表节点,组织成一个信号量链表(IPC 对象定时器像或线程对象分好几种状态,只维护一个IPC对象链表即可)。 rt_semaphore对象有两个私有成员,但一个保留未用,相当于只有一个私有成员rt_semaphore.value,信号量的最大值是65535,有成员类型rt_uint16_t决定。 信号量接口函数 信号量控制块中含有信号量相关的重要参数,在各种信号量状态间纽带的作用。信号量相关接口如下图所示,对一个信号量的操作包含:创建/初始化信号量、获取先看信号量、释放信号量、删除/脱离量信号量。 先看信号的构造/分析构造函数原型(依旧是发光对象与动态对象) // rt-thread-4.0.1includertdef.h /** * This function will initialize a semaphore and put it under control of * resource management. * * @param sem the semaphore object * @param name the name of semaphore * @param value the init value of semaphore * @param flag the flag of semaphore * * @return the operation status, RT_EOK on successful */ rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag); /** * This function will detach a semaphore from resource management * * @param sem the semaphore object * * @return the operation status, RT_EOK on successful * * @see rt_sem_delete */ rt_err_t rt_sem_detach(rt_sem_t sem); /** * This function will create a semaphore from system resource * * @param name the name of semaphore * @param value the init value of semaphore * @param flag the flag of semaphore * * @return the created semaphore, RT_NULL on error happen * * @see rt_sem_init */ rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag); /** * This function will delete a semaphore object and release the memory * * @param sem the semaphore object * * @return the error code * * @see rt_sem_detach */ rt_err_t rt_sem_delete(rt_sem_t sem); 再信号量的获取rt_sem_take与释放rt_sem_release看原函数: // rt-thread-4.0.1includertdef.h /** * This function will take a semaphore, if the semaphore is unavailable, the * thread shall wait for a specified time. * * @param sem the semaphore object * @param time the waiting time * * @return the error code */ rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time); /** * This function will try to take a semaphore and immediately return * * @param sem the semaphore object * * @return the error code */ rt_err_t rt_sem_trytake(rt_sem_t sem); /** * This function will release a semaphore, if there are threads suspended on * semaphore, it will be waked up. * * @param sem the semaphore object * * @return the error code */ rt_err_t rt_sem_release(rt_sem_t sem); 看信号量控制函数rt_sem_control,前面介绍的IPC对象时也说了两个对象宏定义,IPC支持的最后控制命令只有一种RT_IPC_CMD_RESET,对于RT_IPC_CMD_UNKNOWN可能是保留备用吧。rt_sem_control的作用是沉重的信号量值,函数原型如下: // rt-thread-4.0.1includertdef.h /** * This function can get or set some extra attributions of a semaphore object. * * @param sem the semaphore object * @param cmd the execution command * @param arg the execution argument * * @return the error code */ rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg); 信号量是一种非常灵活的同步方式,可以运用在多种场合中,形成锁,同步,资源计数等关系,也能方便的用于线程与线程,中断与线程间的同步中。中断与线程间的互通拒绝采用信号量(锁开关)的方式,而应采用中断的方式。 一般资源计数类型多是混合方式的线程间同步,因为只有个别资源存在线程间的线程间同步处理,需要继续进行线程间同步访问。需要对一个单独的资源进行访问,处理,并进行锁方式的互斥操作。 2.2量互斥对象管理 互斥量又叫相互排斥的信号量,是一种特殊的二值信号量互斥量。和信号量不同的是:拥有互斥量的自己拥有互斥量的实现,互斥量支持访问且能阻止优先级婴儿;并且互斥量只能由持有释放,而信号量可以由任何线程释放。 互斥量的状态只有两种,锁或闭锁(两种状态值)。 ,如果对方互斥量进行开锁,拒绝它的实例。当一个占用量,其他线程将不能够进行开锁或持有它,持有该互斥量的线程也不能获得这个不锁而不被挂起,如下图时所示。这个特性与一般的二值信号量有很大的不同:在信号量中,因为已经不存在实例,循环启动会发生主动挂起(最终形成锁) 使用信号量会的另一个潜在问题是实例级别的防火墙问题,优先级出现在UCOS间任务同步与通信中介绍过,这里不再赘述了,RT-Thread与UCOS类似,都是采用优先级继承算法来解决优先级翻转问题的。在获得互斥量后,请尽快释放互斥量,并且在持有互斥量的过程中,不得再行更改持有互斥量线程的优先级。 互斥量控制块 在RT-Thread,互斥量控制块中是用于管理互斥量的一个,由结构struct rt_mutex结构表示。另一种数据表达方式rt_mutex_t,表示是互斥量的句柄,在C语言中的实现是指相互排斥量控制块的实现。 // rt-thread-4.0.1includertdef.h /** * Mutual exclusion (mutex) structure */ struct rt_mutex { struct rt_ipc_object parent; /**《 inherit from ipc_object */ rt_uint16_t value; /**《 value of mutex */ rt_uint8_t original_priority; /**《 priority of last thread hold the mutex */ rt_uint8_t hold; /**《 numbers of thread hold the mutex */ struct rt_thread *owner; /**《 current owner of mutex */ }; typedef struct rt_mutex *rt_mutex_t; typedef struct rt_mutex *rt_mutex_t; rt_mutex 对象从中生,rt_mutex.parent.parent.type 值为RT_Object_Class_Mutex,rt_mutex.parent.parent.list 互斥量对象链表节点,所有互斥量组织成一个互斥量对象链表。 rt_mutex 祖母绿组件比信号量多,rt_mutex.value 为互斥量值,一般为0或1;rt_mutex.original_priority为抓线程的原始优先级,用于优先级继承算法;rt_mutex.hold为持有线程的持有次数,互斥量支持持有所有权; 拥有多个持有权;所有者当前拥有互斥量的线程地址。 互斥量接口函数 互斥量控制块中具有互斥量控制的重要参数,在互斥量功能中实现重要的作用。互斥量相关如下接口图,对一个互斥量的操作包含:创建/初始化互斥量,获取互斥量,释放互斥量,删除/脱离互斥量。 先看互斥量构造,析构函数原型: // rt-thread-4.0.1srcipc.c /** * This function will initialize a mutex and put it under control of resource * management. * * @param mutex the mutex object * @param name the name of mutex * @param flag the flag of mutex * * @return the operation status, RT_EOK on successful */ rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag); /** * This function will detach a mutex from resource management * * @param mutex the mutex object * * @return the operation status, RT_EOK on successful * * @see rt_mutex_delete */ rt_err_t rt_mutex_detach(rt_mutex_t mutex); /** * This function will create a mutex from system resource * * @param name the name of mutex * @param flag the flag of mutex * * @return the created mutex, RT_NULL on error happen * * @see rt_mutex_init */ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag); /** * This function will delete a mutex object and release the memory * * @param mutex the mutex object * * @return the error code * * @see rt_mutex_detach */ rt_err_t rt_mutex_delete(rt_mutex_t mutex); 接下来看互斥量的获取rt_mutex_take与释放rt_mutex_release函数: // rt-thread-4.0.1srcipc.c /** * This function will take a mutex, if the mutex is unavailable, the * thread shall wait for a specified time. * * @param mutex the mutex object * @param time the waiting time * * @return the error code */ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time); /** * This function will release a mutex, if there are threads suspended on mutex, * it will be waked up. * * @param mutex the mutex object * * @return the error code */ rt_err_t rt_mutex_release(rt_mutex_t mutex); 因为互斥量的获取与释放一般在同一个内部,互斥量值在初始化时设定1,所以互斥量构造函数不需要互斥量值参数,互斥量控制方法正确互斥 量的使用比较单一,因为它是信号量的一种,而且它是锁的存在形式。在初始化的时候,互斥量永远不会实现控制。都处于开锁的状态,而被线程占用的时候则立刻转为闭锁的状态,需要切记是互斥量不能在中断服务例程中使用(中断服务例程使用中断达到互不中断目的)。互斥量更适合于: 线程多次持有互斥量的情况。这样可以导致同一多个集合的持有而导致死锁。 可能会因为多线程同步而导致优先级的情况。 2.3 事件集对象管理 事件集主要用于线程间的同步,与信号量不同,它的特点是可以实现对等多,多对多的同步。任意一个事件唤醒处理线程,或几个事件都到达后才启动线程进行一次紧急事件;同样,事件也可以是多个线程同步事件。这种多个事件的集合可以用一个 32 位无整符号型变量来表示,变量的各个位代表一个事件,通过“逻辑与”或“逻辑或”将一个或多个事件关联起来,形成事件组合。事件的“逻辑或”也是独立类型同步,指的是线程与任何事件之一发生同步;事件“逻辑与”也称为是关联型同步,指的是线程与若干事件都发生同步。 RT-螺纹定义的事件集有以下特点: 事件只与线程相关,事件间相互独立:每个线程可拥有32个事件标志,采用一个32位无符号整型数据进行记录,每一个代表一个事件; 事件仅用于同步,不提供数据传输功能; 事件无事件性,即多个向线程发送同一事件,其效果等同于只发送一次。 在RT-Thread中,线程都拥有一个事件信息标记,它有三个属性,分别是RT_EVENT_FLAG_AND(逻辑与),RT_EVENT_FLAG_OR(逻辑或)以及RT_EVENT_FLAG_CLEAR(清除标记)。当线程等待事件同步时,可以通过32个事件标志和事件信息标记来判断当前接收这个事件是否满足同步条件。 如上图所示,线程#1的事件标志中第1位和第30位被置位,如果事件信息标记位设为设为逻辑与,表示则线程 #1 只有在事件 1 和事件 30 以后才会被触发,如果事件信息标志性事件位设为逻辑,则事件 1 或 30 中的任意一个发生触发唤醒线程# 1。如果信息标志同时设置了清除标记位,则当线程#1 唤醒后将主动把事件1 和事件30 清零,其他事件标志将继续存在(即置1)。 事件集控制块 在 RT-Thread 中,事件集控制块表示操作系统管理事件的一个数据结构,由结构体 struct rt_event 。另一种 C 表达方式 rt_event_t,表示是事件集的句柄,在 C 语言中的实现是事件集控制块的轨迹。事件集控制块结构的详细定义请见以下代码: // rt-thread-4.0.1includertdef.h /* * event structure */ struct rt_event { struct rt_ipc_object parent; /**《 inherit from ipc_object */ rt_uint32_t set; /**《 event set */ }; typedef struct rt_event *rt_event_t; /** * flag defintions in event */ #define RT_EVENT_FLAG_AND 0x01 /**《 logic and */ #define RT_EVENT_FLAG_OR 0x02 /**《 logic or */ #define RT_EVENT_FLAG_CLEAR 0x04 /**《 clear flag */ /** * Thread structure */ struct rt_thread { 。..。.. #if defined(RT_USING_EVENT) /* thread event */ rt_uint32_t event_set; rt_uint8_t event_info; #endif 。..。.. }; typedef struct rt_thread *rt_thread_t; rt_event对象也只有一个私有成员,rt_event.set表示一个32位的事件集合,每个位标识一个事件,比特值可以标记某事件是否发生。 由于事件集要实现一对多,多对多的线程间同步,只靠事件集对象满足需求,还要求线程对象配合管理自己所需要的事件集。对象配合事件集实现该同步的关键。rt_thread.event_set 表示线程等待的事件集;rt_thread.event_info 表示该线程事件集中的多个事件的逻辑组合,取值与上面标志定义一致性,可以是 RT_EVENT_FLAG_AND / OR / CLEAR 。 事件集接口函数 先看事件集的构造、构造原理: // rt-thread-4.0.1srcipc.c /** * This function will initialize an event and put it under control of resource * management. * * @param event the event object * @param name the name of event * @param flag the flag of event * * @return the operation status, RT_EOK on successful */ rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag); /** * This function will detach an event object from resource management * * @param event the event object * * @return the operation status, RT_EOK on successful */ rt_err_t rt_event_detach(rt_event_t event); /** * This function will create an event object from system resource * * @param name the name of event * @param flag the flag of event * * @return the created event, RT_NULL on error happen */ rt_event_t rt_event_create(const char *name, rt_uint8_t flag); /** * This function will delete an event object and release the memory * * @param event the event object * * @return the error code */ rt_err_t rt_event_delete(rt_event_t event); 接下来看事件集的发送rt_event_send与接收rt_event_recv函数原型: // rt-thread-4.0.1srcipc.c /** * This function will send an event to the event object, if there are threads * suspended on event object, it will be waked up. * * @param event the event object * @param set the event set * * @return the error code */ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set); /** * This function will receive an event from event object, if the event is * unavailable, the thread shall wait for a specified time. * * @param event the fast event object * @param set the interested event set * @param option the receive option, either RT_EVENT_FLAG_AND or * RT_EVENT_FLAG_OR should be set. * @param timeout the waiting time * @param recved the received event, if you don‘t care, RT_NULL can be set. * * @return the error code */ rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t *recved); 事件集发送函数rt_event_send先将发送的事件集保存到事件集对象事件中,再遍历已挂起线程链表,根据各线程等待的事件组合判断该线程是否已经满足条件,若挂起线程等待的事件组合都已收到,那么 事件集控制函数实际上就是创建事件集对象值,即事件集也把事件集对象的事件集rt_event.set为0(事件集对象构造时也)将其置为0),rt_event_control的函数原型如下: // rt-thread-4.0.1srcipc.c /** * This function can get or set some extra attributions of an event object. * * @param event the event object * @param cmd the execution command * @param arg the execution argument * * @return the error code */ rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg); 一个线程或中断服务实例程序发送一个事件集对象,而后等待的线程被线程间同步。但它与信号量不同的是,事件的发送操作在未清除前,是不可累积的,而信号量的释放动作是累积的。事件的另一个特性是,接收线程可等待多。一种事件,即多个事件对应一个线程或多个线程。单一个体的释放动作,而不能同时等待多种类型的释放。如下图所示为多事件响应: 一个事件集中包含 32 个事件,某个线程只等待、接收它关注的事件。是一个线程等待多个事件的事件(线程 1、2 个同时等待多个事件,事件间可以使用“与”或者“或” ”逻辑触发线程),也可以是多个线程等待一个事件的发生(事件 25)。当有它们关注的事件发生时,线程将被启动并进行处理动作。 |
|
|
|
只有小组成员才能发言,加入小组>>
878浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 15:57 , Processed in 0.418660 second(s), Total 49, Slave 39 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号