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内核代码中最常见的模式之一,理解并正确使用它们对于编写高效、安全的内核代码至关重要。

相关推荐
淼淼爱喝水2 小时前
openEuler 下 Ansible 基础命令详解与实操演示2
linux·运维·windows
拾贰_C2 小时前
【Ubuntu | install | 安装软件】 Ubuntu软件安装多种方式以及卸载
linux·运维·ubuntu
·醉挽清风·3 小时前
学习笔记—Linux—信号阻塞&信号捕捉
linux·笔记·学习
杨云龙UP3 小时前
Linux生产环境下Oracle RMAN 备份、核查、清理与验证常用命令整理_20260330
linux·运维·服务器·数据库·oracle
A.A呐3 小时前
【Linux第二十二章】https
linux·https
齐齐大魔王4 小时前
linux-线程编程
java·linux·服务器
吕司5 小时前
Linux动静态库
linux·运维·服务器
123过去5 小时前
mfcuk使用教程
linux·测试工具·安全
风曦Kisaki5 小时前
#Linux进阶Day05:防火墙+VMware网络+sshd远程管理
linux·运维