/** * rt_container_of - return the member address of ptr, if the type of ptr is the * struct type. */ #define rt_container_of(ptr, type, member) ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) /** * @brief get the struct for this entry * @param node the entry point 表示一个节点的地址 * @param type the type of structure 该节点所在的结构体的类型 * @param member the name of list in structure 该节点在该结构体中的成员名称 */ #define rt_list_entry(node, type, member) rt_container_of(node, type, member)
具体实现算法:
我们知道了一个节点 tlist 的地址 ptr,现在要推算出该节点所在的 type 类型的结构体的起始地址 f_struct_ptr。我们可以将 ptr的值减去图中灰色部分的偏移的大小就可以得到 f_struct_ptr 的地址,现在的关键是如何计算出灰色部分的偏移大小。这里采取的做法是将 0 地址强制类型类型转换为 type,即(type *)0,然后通过指针访问结构体成员的方式获取到偏移的大小,即(&((type *)0)->member),最后即可算出 f_struct_ptr = ptr -(&((type *)0)->member)。
这个偏移的计算和<stddef.h>中的宏offsetof(s,m)一样
用法举例:
/* 启动系统调度器 */ void rt_system_scheduler_start(void) { register struct rt_thread *to_thread; /* 手动指定第一个运行的线程 */ (1) to_thread = rt_list_entry(rt_thread_priority_table[0].next,struct rt_thread,tlist); rt_current_thread = to_thread; (2)//将获取到的第一个要运行的线程控制块指针传到全局变量rt_current_thread 中 /* 切换到第一个线程,该函数在 context_rvds.S 中实现, 在 rthw.h 声明,用于实现第一次线程切换。 当一个汇编函数在 C 文件中调用的时候,如果有形参, 则执行的时候会将形参传人到 CPU 寄存器 r0。*/ rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp); (3) }
这里只关心rt_list_entry的用法,rt_thread的结构体定义如下(简化了的)。要获取一个rt_thread,则根据链表通过rt_list_entry获得对应的线程。
struct rt_thread { void *sp; /* 线程栈指针 */ void *entry; /* 线程入口地址 */ void *parameter; /* 线程形参 */ void *stack_addr; /* 线程栈起始地址 */ rt_uint32_t stack_size; /* 线程栈大小,单位为字节 */ rt_list_t tlist; /* 线程链表节点 */ (1) };