1. container_of 宏
解释:
container_of 是一个经典的内核宏,用于从结构体的某个成员的指针,计算出该成员所在结构体的指针。这个宏通常用于链表操作中,获取链表节点所在的结构体指针。
示例代码:
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)((char *)__mptr - offsetof(type, member)); })
解释:
ptr是指向结构体某个成员的指针。type是结构体的类型。member是结构体中的成员。
container_of 的作用是,利用 offsetof 宏(该宏返回结构体成员相对于结构体开头的偏移量),通过传入成员指针,计算出整个结构体指针。这是因为 C 语言中的结构体成员在内存中的排列是连续的,通过计算成员的偏移量,可以反向推导出结构体的起始位置。
使用场景:
通常用于链表操作中。当你遍历链表时,链表节点是结构体中的一部分,而 container_of 使得我们可以从链表节点中的指针反推出整个结构体指针。
例子:
假设有如下链表结构:
struct node {
int data;
struct list_head list; // 链表指针
};
struct node *n;
struct list_head *head = &n->list;
n = container_of(head, struct node, list);
这里,n 就是结构体 node 的指针,list 是它的链表成员。通过 container_of 宏,从链表的成员 list 指针中计算出完整的 node 结构体指针。
2. list_for_each_entry 宏
解释:
list_for_each_entry 是 Linux 内核中用于遍历链表的宏。它让链表遍历变得更加简洁,同时通过 container_of 实现类型安全。
示例代码:
#define list_for_each_entry(ptr, head, member) \
for (ptr = list_entry((head)->next, typeof(*ptr), member); \
&ptr->member != (head); \
ptr = list_entry(ptr->member.next, typeof(*ptr), member))
解释:
ptr是链表中的当前元素。head是链表的头指针。member是结构体中指向链表节点的成员变量。
这个宏通过 list_entry 宏获取当前链表节点中的 ptr,并使用 typeof(*ptr) 确定当前元素的类型。链表中的每个节点通常是一个结构体的成员,这个宏会遍历链表,自动跳转到结构体的下一个成员。
使用场景:
这种宏常常用于内核中遍历链表,执行特定操作。它的好处是,内核中的链表遍历是高效且类型安全的。
例子:
假设你有如下链表结构和遍历需求:
struct node {
int data;
struct list_head list;
};
struct list_head head;
struct node *n;
list_for_each_entry(n, &head, list) {
printf("%d\n", n->data);
}
这个宏将遍历链表中的所有 node 结构体,并打印每个节点的 data。
3. _Generic 宏(C11)
解释:
_Generic 是 C11 中新增的特性,可以根据表达式的类型选择不同的代码路径。它类似于 C++ 中的模板类型推导,但它在编译时就决定了具体类型的处理。
示例代码:
#define print_value(value) \
_Generic((value), \
int: printf("Integer: %d\n", value), \
float: printf("Float: %.2f\n", value), \
double: printf("Double: %.2f", value), \
char: printf("Char: %c\n", value) \
)
int main() {
print_value(42); // 打印 Integer: 42
print_value(3.14f); // 打印 Float: 3.14
print_value(3.14);
print_value('A'); // 打印 Char: A
return 0;
}
解释:
print_value是一个宏,它根据value的类型选择不同的打印格式。_Generic是 C11 标准中的一个关键字,可以根据传入参数的类型来选择不同的操作。
通过 _Generic,内核代码可以根据不同的数据类型调用不同的操作。这个特性在内核中尤为有用,因为内核需要处理各种硬件和数据结构,而使用 _Generic 可以写出更加通用且类型安全的代码。
使用场景:
内核中的各种设备驱动和数据操作需要对不同类型的数据进行不同的处理,使用 _Generic 可以使代码更加简洁和高效。
4. offsetof 宏
解释:
offsetof 是一个非常有用的宏,用于计算结构体成员相对于结构体起始位置的偏移量。
示例代码:
#define offsetof(type, member) ((size_t)&(((type *)0)->member))
解释:
offsetof(type, member)计算结构体type中成员member相对于结构体首地址的字节偏移量。
这个宏用于结构体中访问成员时,计算成员相对位置。它通常与 container_of 一起使用。
使用场景:
在 Linux 内核中,offsetof 和 container_of 宏常常配合使用,尤其是在链表操作中,帮助从成员指针计算出完整的结构体指针。
5. ALIGN 宏
解释:
ALIGN 宏用于内存对齐,确保数据在内存中的正确位置。这对于性能优化和硬件兼容性至关重要。
示例代码:
#define ALIGN_UP(x, align) (((x) + (align) - 1) & ~((align) - 1))
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
解释:
ALIGN_UP(x, align)将x上调到最接近的align的倍数。ALIGN_DOWN(x, align)将x下调到最接近的align的倍数。
内存对齐是为了提高 CPU 访问内存的效率,不同的架构对对齐有不同的要求。这个宏可以帮助确保数据按要求对齐,避免性能下降或硬件错误。
使用场景:
内核中操作设备、内存映射、DMA(直接内存访问)等任务时,通常会使用这些宏来确保数据正确对齐。
总结
通过这些宏,内核能够实现类型安全、性能优化以及跨平台支持。宏的灵活性使得内核代码更加高效和通用,但也带来了一些调试上的挑战。宏在内核中非常重要,因为它们能够处理不同类型的数据、避免冗余代码,并确保代码在不同硬件架构上运行良好。