Linux 内核遍历宏介绍

Linux内核中的遍历宏全面详解

Linux内核中大量使用遍历宏(Iteration Macros)来简化数据结构的遍历操作。这些宏提供了类型安全、简洁且高效的遍历方式,是内核编程的核心范式之一。


一、遍历宏的分类

1.1 按功能分类

复制代码
Linux内核遍历宏
├── 链表遍历宏
│   ├── list_for_each_entry        // 遍历标准链表
│   ├── hlist_for_each_entry       // 遍历哈希链表
│   ├── list_for_each_entry_safe   // 安全遍历(可删除)
│   └── list_for_each_entry_rcu    // RCU保护遍历
│
├── CPU遍历宏
│   ├── for_each_possible_cpu      // 所有可能的CPU
│   ├── for_each_online_cpu        // 在线CPU
│   ├── for_each_present_cpu       // 存在的CPU
│   └── for_each_cpu_and           // CPU掩码交集
│
├── 进程遍历宏
│   ├── for_each_process           // 遍历所有进程
│   ├── for_each_thread            // 遍历进程的线程
│   └── for_each_task_pid          // 遍历特定PID的进程
│
├── 内存遍历宏
│   ├── for_each_sg                // 遍历scatter-gather列表
│   ├── for_each_vma               // 遍历虚拟内存区域
│   └── for_each_zone              // 遍历内存区域
│
├── 文件系统遍历宏
│   ├── for_each_mount_point       // 遍历挂载点
│   ├── for_each_dentry            // 遍历目录项
│   └── for_each_inode             // 遍历inode
│
├── 设备遍历宏
│   ├── for_each_netdev            // 遍历网络设备
│   ├── for_each_pci_dev           // 遍历PCI设备
│   └── for_each_irq               // 遍历中断
│
└── 定时器遍历宏
    ├── for_each_timer             // 遍历定时器
    └── for_each_hrtimer           // 遍历高精度定时器

二、链表遍历宏(最常用)

2.1 标准链表遍历

c 复制代码
#include <linux/list.h>

struct my_struct {
    int data;
    struct list_head list;  // 链表节点
};

struct list_head head;
INIT_LIST_HEAD(&head);

// 1. 基本遍历(不可删除节点)
struct my_struct *entry;
list_for_each_entry(entry, &head, list) {
    printk("data: %d\n", entry->data);
}

// 2. 安全遍历(可删除当前节点)
struct my_struct *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &head, list) {
    if (entry->data == 0) {
        list_del(&entry->list);
        kfree(entry);
    }
}

// 3. 反向遍历
list_for_each_entry_reverse(entry, &head, list) {
    printk("data: %d\n", entry->data);
}

// 4. 从指定位置开始遍历
struct my_struct *entry;
list_for_each_entry_from(entry, &head, list) {
    // 从entry开始继续遍历
}

// 5. 带锁的安全遍历
spin_lock(&lock);
list_for_each_entry(entry, &head, list) {
    // 在锁保护下遍历
}
spin_unlock(&lock);

2.2 RCU保护的链表遍历

c 复制代码
#include <linux/rculist.h>

struct my_struct {
    int data;
    struct list_head list;
    struct rcu_head rcu;
};

struct list_head head;

// RCU读锁保护
rcu_read_lock();
struct my_struct *entry;
list_for_each_entry_rcu(entry, &head, list) {
    printk("data: %d\n", entry->data);
}
rcu_read_unlock();

// RCU安全删除
list_del_rcu(&entry->list);
call_rcu(&entry->rcu, my_struct_free);

2.3 哈希链表遍历

c 复制代码
#include <linux/list.h>

#define HASH_SIZE 256
struct hlist_head hash_table[HASH_SIZE];

struct my_struct {
    int key;
    struct hlist_node node;  // 哈希链表节点
};

// 遍历单个哈希桶
struct my_struct *entry;
struct hlist_head *head = &hash_table[hash_index];
hlist_for_each_entry(entry, head, node) {
    if (entry->key == target_key) {
        // 找到目标
        break;
    }
}

// 安全遍历(可删除)
struct my_struct *entry, *tmp;
hlist_for_each_entry_safe(entry, tmp, head, node) {
    if (entry->key == target_key) {
        hlist_del(&entry->node);
        kfree(entry);
    }
}

// RCU保护的哈希链表遍历
rcu_read_lock();
hlist_for_each_entry_rcu(entry, head, node) {
    printk("key: %d\n", entry->key);
}
rcu_read_unlock();

2.4 链表遍历宏详解

宏名称 用途 是否可删除节点 线程安全
list_for_each_entry 标准正向遍历 需外部锁
list_for_each_entry_safe 安全遍历(支持删除) 需外部锁
list_for_each_entry_reverse 反向遍历 需外部锁
list_for_each_entry_rcu RCU保护遍历 RCU保护
list_for_each_entry_safe_rcu RCU安全遍历 RCU保护
hlist_for_each_entry 哈希链表遍历 需外部锁
hlist_for_each_entry_safe 哈希链表安全遍历 需外部锁

三、CPU遍历宏

3.1 基本CPU遍历

c 复制代码
#include <linux/cpumask.h>

// 1. 遍历所有可能的CPU
int cpu;
for_each_possible_cpu(cpu) {
    struct per_cpu_data *data = per_cpu_ptr(&my_data, cpu);
    init_percpu_data(data);
}

// 2. 遍历在线CPU
for_each_online_cpu(cpu) {
    smp_call_function_single(cpu, work_func, NULL, 1);
}

// 3. 遍历存在的CPU
for_each_present_cpu(cpu) {
    init_cpu_hardware(cpu);
}

// 4. 遍历活跃CPU(用于调度)
for_each_active_cpu(cpu) {
    assign_task_to_cpu(cpu);
}

// 5. 遍历CPU掩码的交集
for_each_cpu_and(cpu, cpu_online_mask, cpu_active_mask) {
    // 处理既在线又活跃的CPU
}

3.2 CPU遍历宏详解

c 复制代码
// 实现原理
#define for_each_possible_cpu(cpu) \
    for_each_cpu((cpu), cpu_possible_mask)

#define for_each_cpu(cpu, mask) \
    for ((cpu) = -1; \
         (cpu) = cpumask_next((cpu), (mask)), \
         (cpu) < nr_cpu_ids;)

// 实际使用示例
void show_cpu_status(void)
{
    int cpu;
    char buf[128];
    
    // 打印在线CPU列表
    cpumap_print_to_pagebuf(true, buf, cpu_online_mask);
    pr_info("Online CPUs: %s\n", buf);
    
    // 为每个在线CPU执行操作
    for_each_online_cpu(cpu) {
        pr_info("CPU%d: status=%s\n", cpu, 
                cpu_online(cpu) ? "online" : "offline");
    }
}

四、进程遍历宏

4.1 进程遍历

c 复制代码
#include <linux/sched.h>

// 1. 遍历所有进程
struct task_struct *p;
for_each_process(p) {
    printk("Process: %s (PID: %d)\n", p->comm, p->pid);
}

// 2. 遍历进程的所有线程
struct task_struct *thread;
for_each_thread(p, thread) {
    printk("Thread: %s (TID: %d)\n", thread->comm, thread->pid);
}

// 3. 遍历特定PID命名空间的进程
struct pid_namespace *pid_ns = task_active_pid_ns(current);
struct task_struct *p;
for_each_process_in_pid_ns(pid_ns, p) {
    // 处理该命名空间的进程
}

// 4. 安全遍历(防止进程退出)
struct task_struct *p, *tmp;
read_lock(&tasklist_lock);
for_each_process_safe(p, tmp) {
    // 在锁保护下遍历,可以安全地处理
    get_task_struct(p);  // 增加引用计数
    // ... 处理进程 ...
    put_task_struct(p);
}
read_unlock(&tasklist_lock);

4.2 进程遍历注意事项

c 复制代码
// 错误:遍历时可能导致进程退出
for_each_process(p) {
    // 进程p可能在这之后退出
    printk("%s\n", p->comm);  // 危险!
}

// 正确:使用RCU保护
rcu_read_lock();
for_each_process(p) {
    // RCU保护下,进程不会退出
    printk("%s\n", p->comm);
}
rcu_read_unlock();

// 正确:使用引用计数
struct task_struct *p;
for_each_process(p) {
    get_task_struct(p);  // 防止进程退出
    // 安全使用p
    put_task_struct(p);
}

五、内存区域遍历宏

5.1 内存区域遍历

c 复制代码
#include <linux/mm.h>

// 遍历进程的虚拟内存区域
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;

down_read(&mm->mmap_lock);
for_each_vma(vma, mm) {
    printk("VMA: 0x%lx-0x%lx flags=0x%lx\n",
           vma->vm_start, vma->vm_end, vma->vm_flags);
}
up_read(&mm->mmap_lock);

// 遍历内存节点
int nid;
for_each_node(nid) {
    struct pglist_data *pgdat = NODE_DATA(nid);
    printk("Node %d: start_pfn=0x%lx\n", nid, pgdat->node_start_pfn);
}

// 遍历内存区域
struct zone *zone;
for_each_zone(zone) {
    printk("Zone: %s, pages=%lu\n", 
           zone->name, zone->managed_pages);
}

六、设备遍历宏

6.1 网络设备遍历

c 复制代码
#include <linux/netdevice.h>

// 遍历所有网络设备
struct net_device *dev;
for_each_netdev(&init_net, dev) {
    printk("Device: %s, state=%d\n", dev->name, dev->state);
}

// 安全遍历(可删除设备)
struct net_device *dev, *tmp;
for_each_netdev_safe(&init_net, dev, tmp) {
    if (should_remove(dev)) {
        unregister_netdev(dev);
    }
}

// 遍历特定命名空间的网络设备
struct net *net = current->nsproxy->net_ns;
for_each_netdev(net, dev) {
    // 处理该命名空间的设备
}

6.2 PCI设备遍历

c 复制代码
#include <linux/pci.h>

// 遍历所有PCI设备
struct pci_dev *pdev;
for_each_pci_dev(pdev) {
    printk("PCI: %04x:%04x\n", pdev->vendor, pdev->device);
}

// 遍历特定总线的设备
struct pci_bus *bus;
for_each_pci_bus(bus) {
    printk("PCI Bus: %02x\n", bus->number);
}

七、文件系统遍历宏

7.1 挂载点遍历

c 复制代码
#include <linux/mount.h>

// 遍历所有挂载点
struct mount *mnt;
for_each_mount(mnt) {
    printk("Mount: %s on %s\n", 
           mnt->mnt_devname, mnt->mnt.mnt_root->d_name.name);
}

7.2 目录项遍历

c 复制代码
#include <linux/dcache.h>

// 遍历目录下的所有目录项
struct dentry *parent = dir->dentry;
struct dentry *dentry;

spin_lock(&parent->d_lock);
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
    printk("Dentry: %s\n", dentry->d_name.name);
}
spin_unlock(&parent->d_lock);

八、定时器遍历宏

8.1 定时器遍历

c 复制代码
#include <linux/timer.h>

// 遍历定时器(调试用)
struct timer_list *timer;
struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);

spin_lock(&base->lock);
for_each_timer(timer, base) {
    printk("Timer: expires=%lu, function=%pS\n", 
           timer->expires, timer->function);
}
spin_unlock(&base->lock);

九、自定义遍历宏

9.1 创建自定义遍历宏

c 复制代码
// 1. 定义数据结构
struct my_array {
    int *items;
    int size;
};

// 2. 创建遍历宏
#define for_each_my_array(item, array) \
    for (int __i = 0; \
         (__i < (array)->size) && ((item) = &(array)->items[__i], 1); \
         __i++)

// 3. 使用
struct my_array arr;
int *item;
for_each_my_array(item, &arr) {
    printk("Item: %d\n", *item);
}

// 4. 带索引的遍历宏
#define for_each_my_array_idx(item, idx, array) \
    for ((idx) = 0; \
         ((idx) < (array)->size) && ((item) = &(array)->items[idx], 1); \
         (idx)++)

// 使用
int idx;
for_each_my_array_idx(item, idx, &arr) {
    printk("arr[%d] = %d\n", idx, *item);
}

9.2 复杂数据结构的遍历宏

c 复制代码
// 树形结构遍历宏
struct tree_node {
    struct tree_node *parent;
    struct list_head children;
    int data;
};

#define for_each_tree_child(child, parent) \
    list_for_each_entry(child, &(parent)->children, node)

#define for_each_tree_descendant(node, root) \
    for ((node) = (root); (node); (node) = next_tree_node(node))

// 使用
struct tree_node *root;
struct tree_node *child;
struct tree_node *desc;

for_each_tree_child(child, root) {
    printk("Child: %d\n", child->data);
}

for_each_tree_descendant(desc, root) {
    printk("Descendant: %d\n", desc->data);
}

十、遍历宏的性能优化

10.1 预取优化

c 复制代码
// 使用prefetch预取下一个元素
struct my_struct *entry;
list_for_each_entry(entry, &head, list) {
    prefetch(entry->list.next);  // 预取下一个节点
    process_entry(entry);
}

10.2 缓存友好遍历

c 复制代码
// 不好的遍历(跳跃访问)
for_each_possible_cpu(cpu) {
    struct data *d = per_cpu_ptr(&my_data, cpu);
    d->counter++;  // 可能跨CPU缓存行
}

// 好的遍历(本地访问)
int cpu = get_cpu();
struct data *d = this_cpu_ptr(&my_data);
d->counter++;
put_cpu();

十一、常见错误与调试

11.1 遍历时删除节点的错误

c 复制代码
// 错误:遍历时删除导致崩溃
struct my_struct *entry;
list_for_each_entry(entry, &head, list) {
    if (entry->data == 0) {
        list_del(&entry->list);  // 错误!破坏了遍历
        kfree(entry);
    }
}

// 正确:使用_safe版本
struct my_struct *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &head, list) {
    if (entry->data == 0) {
        list_del(&entry->list);
        kfree(entry);
    }
}

11.2 遍历时缺少锁保护

c 复制代码
// 错误:无锁遍历并发修改的链表
list_for_each_entry(entry, &head, list) {
    // 其他CPU可能正在修改链表
    process_entry(entry);
}

// 正确:使用锁保护
spin_lock(&list_lock);
list_for_each_entry(entry, &head, list) {
    process_entry(entry);
}
spin_unlock(&list_lock);

11.3 调试技巧

c 复制代码
// 打印链表信息
void dump_list(struct list_head *head)
{
    struct my_struct *entry;
    int count = 0;
    
    pr_info("List dump:\n");
    list_for_each_entry(entry, head, list) {
        pr_info("  [%d] data=%d, next=%p, prev=%p\n",
                count++, entry->data,
                entry->list.next, entry->list.prev);
    }
    pr_info("Total: %d entries\n", count);
}

// 使用lockdep检查锁
lockdep_assert_held(&list_lock);
list_for_each_entry(entry, &head, list) {
    // 确保锁已持有
}

十二、总结

12.1 遍历宏的核心特点

  1. 类型安全:通过宏参数传递类型,避免强制转换
  2. 简洁高效:减少重复代码,编译器优化好
  3. 模式统一:所有遍历宏遵循相似的使用模式
  4. 上下文感知:有些宏需要特定的锁或RCU保护

12.2 选择指南

场景 推荐遍历宏 原因
标准链表操作 list_for_each_entry 最常用,性能好
需要删除节点 list_for_each_entry_safe 保存了next指针
RCU保护读取 list_for_each_entry_rcu RCU语义正确
哈希表遍历 hlist_for_each_entry 适合哈希桶
CPU相关操作 for_each_online_cpu 只处理在线CPU
进程遍历 RCU保护的遍历 进程可能退出
网络设备 for_each_netdev 网络子系统标准
内存区域 RCU或锁保护 VMA可能改变

12.3 最佳实践

  1. 总是使用正确的遍历宏(普通/安全/RCU)
  2. 在需要时使用适当的锁保护
  3. 避免在遍历中长时间阻塞
  4. 使用prefetch优化长链表遍历
  5. 注意遍历方向对缓存的影响
  6. 自定义遍历宏时保持内核风格

遍历宏是Linux内核代码中最常见的模式之一,理解并正确使用它们对于编写高效、安全的内核代码至关重要。

相关推荐
HLC++14 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室15 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy18 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕18 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫19 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生20 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1121 小时前
linux基础IO
linux·运维·服务器
BelongPanda1 天前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯1 天前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_777998341 天前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器