0
点赞
收藏
分享

微信扫一扫

[008] [RT-Thread学习笔记] 求结构体首地址rt_list_entry函数与字节对齐RT_ALIGN宏

才德的女子 2022-02-08 阅读 26

1 rt_list_entry函数

rt_list_entry函数的作用是根据已知成员的地址,算出其结构体的首地址。函数定义如下(在rtservice.h中):

1.1 源码分析

#define rt_container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))

/**
 * @brief 获取结构体入口地址
 * @param node      入口节点地址
 * @param type      节点所在结构体的类型  
 * @param member    节点在该结构体中的成员名称
 */
#define rt_list_entry(node, type, member) \
    rt_container_of(node, type, member)
  • (unsigned long)(&((type *)0)->member)):其中(type *)0表示将数值0强制转换为type 类型(一般为结构体类型)的指针变量,然后让该指针访问结构体中名称为member的成员。于是&((type *)0)->member)就表示该成员在内存中的地址,由于结构体是按照定义顺序将每个成员放入到内存中的,因此该成员地址等价于结构体的基地址+该成员所在结构体基地址的偏移字节数,结构体基地址在这里就是指针变量(type *)0本身,即为0,所以&((type *)0)->member)就是名为member的成员相对于type类型的结构体首地址偏移的字节数。最后将其转换为无符号长整型。
  • (char *)(ptr)ptr就是实际定义的结构体变量的某成员地址,然后将其转换为char *类型的指针变量。

问:为什么一定要将ptr转换成char*类型呢?
答:因为指针变量加减某值后偏移的字节数取决于它指向的变量类型,如int*类型的指针加1后,是相对于指针变量当前地址向后偏移4个字节。在这里需要将ptr指向的成员,减去该成员相对结构体首地址的偏移字节数(unsigned long)(&((type *)0)->member)),以得到该成员所在结构体变量的地址。所以只能转换成char*的指针去减偏移的字节数,如果用int*类型的指针,那么就会偏移四倍于它的字节,得到一个错误的地址。

1.2 应用示例

如释放互斥量函数rt_mutex_release中有这样一段代码:

/* 获取互斥量等待队列第一个线程的结构体地址 */
thread = rt_list_entry(mutex->parent.suspend_thread.next,
                       struct rt_thread,
                       tlist);

其中rt_thread线程结构体定义为(部分省略):

struct rt_thread
{
    /* rt object */
    char        name[RT_NAME_MAX];         /* 线程名称 */
    rt_uint8_t  type;                      /* 对象类型  */
    rt_uint8_t  flags;                     /* 线程标志位 */
    ...
    rt_list_t   list;                      /* 对象列表 */
    rt_list_t   tlist;                     /* 线程列表 */
    ...
};

mutex->parent.suspend_thread.next表示该互斥量线程挂起链表中第一个节点,为rt_list_t类型,为rt_thread线程结构体中的tlist成员,通过rt_list_entry函数获取该线程的地址,然后让该线程持有互斥量。

2 RT_ALIGN宏

2.1 源码分析

直接分析源码,该宏在rtdef.h中定义:

#define RT_ALIGN(size, align)	(((size) + (align) - 1) & ~((align) - 1))

作用为将字节数size向上提升为align的整数倍,需要注意的是align要为2的n次幂。

  • (size) + (align) - 1align-1size除以align的最大余数,用size去加它是为了便于向上取align的整数倍数值。
  • ~((align) - 1):为余数的掩码,将其与上(size) + (align) - 1即可清除其余数,得到align的整数倍数值。

此外,还有将字节数size向下提升到align的整数倍的宏RT_ALIGN_DOWN:

#define RT_ALIGN_DOWN(size, align)      ((size) & ~((align) - 1))

2.2 应用示例

  • RT_ALIGN(17,4)= 20
(size) + (align) - 1 = 17 + 3 = 0001 0101
~((align) - 1) = ~(3) = ~(0000 0011)
0001 0101 & ~(0000 0011) = 0001 0100 = 20	// 将后两位清0,后两位即为最大余数3

上面说过align必须为2的n次幂,下面测试其不为2的n次幂时的错误用法:

  • RT_ALIGN(17,3)
    正确结果:18
    实际运算结果:17
(size) + (align) - 1 = 17 + 2 = 0001 0011
~((align) - 1) = ~(2) = ~(0000 0010)
0001 0011 & ~(0000 0010) = 0001 0001 = 17	

END

举报

相关推荐

0 条评论