rt_container_of 已知一个结构体里面的成员的地址,反推出该结构体的首地址

/**
 * 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)
 };

 


 

posted @ 2020-10-12 13:42  不明白就去明白  阅读(1270)  评论(0编辑  收藏  举报