1. 程式人生 > 實用技巧 >rt_container_of 已知一個結構體裡面的成員的地址,反推出該結構體的首地址

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)。



用法舉例:

/* 啟動系統排程器 */
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)
 };