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) - 1:align-1为size除以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










