Skip to content

第一百八十八章:设计模式

大乘期

涉及内核源码:

钟楼之后,林小源来到一座宏伟的讲道堂。讲道堂内坐满了修士,上方的高台上站着一位须发皆白的老者,手中握着一柄玉如意。

"内核之道,不仅在于算法和数据结构,更在于设计模式。"老者的声音洪亮而清晰,"设计模式是前人智慧的结晶,是解决常见问题的通用方案。"

他用玉如意在空中一划,虚空中浮现出六个光团,每个光团中都蕴含着不同的符文。

"第一个,链表。"老者指向第一个光团,"内核中最常用的数据结构。但内核的链表不是你想的那样——list_head不是独立的链表,而是嵌入到结构体中的。任何结构体,只要包含list_head成员,就能被链表管理。"

林小源恍然大悟。他之前一直以为链表是独立的数据结构,没想到内核的做法如此巧妙——通过嵌入,一个结构体可以同时属于多个链表。

"第二个,通知链。"老者指向第二个光团,"事件发生时,通知所有注册的回调函数。比如网络接口状态变化时,通知所有关心这个事件的模块。"

"第三个,工作队列。延迟执行,把工作推到进程上下文中处理。中断处理程序不能做太多事,就把剩下的工作交给工作队列。"

"第四个,状态机。网络协议的状态转换、文件系统的挂载状态……内核中到处都是状态机。"

老者顿了顿,目光扫过全场。"第五个,引用计数kref。当引用计数降为零时,自动释放资源。防止内存泄漏的利器。"

"第六个,也是最重要的——回调函数。"老者的语气变得庄重,"file_operations就是回调函数的典范。每个文件系统、每个设备驱动,都通过file_operations注册自己的open、read、write、close函数。VFS不需要知道具体的实现,只需要通过函数指针调用。"

"这就是策略模式?"林小源脱口而出。

老者赞许地点头。"没错。VFS是上下文,file_operations是策略接口,ext4、procfs、sysfs是具体策略。这就是接口与实现分离——内核可扩展性的根基。"

他收起玉如意,环顾讲道堂。"记住这些模式。它们不是教科书上的理论,而是内核中每天都在使用的实战技巧。"

c
/*
 * 内核设计模式:
 *
 * 1. 链表 (list_head)
 *    内核最常用的数据结构
 *    嵌入到结构体中
 *    宏操作
 *
 * 2. 通知链 (notifier_chain)
 *    事件通知机制
 *    注册回调函数
 *
 * 3. 工作队列 (workqueue)
 *    延迟执行
 *    进程上下文
 *
 * 4. 状态机
 *    网络协议
 *    文件系统状态
 *
 * 5. 引用计数
 *    kref
 *    防止内存泄漏
 *
 * 6. 回调函数
 *    file_operations
 *    策略模式
 */

/* 1. 链表模式 */
struct list_head {
    struct list_head *next, *prev;
};

struct task_struct {
    int pid;
    char comm[16];
    struct list_head list;
};

/* 2. 通知链模式 */
typedef void (*notifier_fn_t)(void *data);

struct notifier_block {
    notifier_fn_t notifier_call;
    struct notifier_block *next;
};

/* 3. 引用计数模式 */
struct kref {
    int refcount;
};

void kref_init(struct kref *kref) {
    kref->refcount = 1;
}

void kref_get(struct kref *kref) {
    kref->refcount++;
}

int kref_put(struct kref *kref, void (*release)(struct kref *)) {
    kref->refcount--;
    if (kref->refcount == 0) {
        release(kref);
        return 1;
    }
    return 0;
}

/* 4. 回调函数模式 */
struct file_operations {
    int (*open)(void);
    int (*read)(void);
    int (*write)(void);
    int (*close)(void);
};

int my_open(void) { printf("    open\n"); return 0; }
int my_read(void) { printf("    read\n"); return 0; }
int my_write(void) { printf("    write\n"); return 0; }
int my_close(void) { printf("    close\n"); return 0; }

struct file_operations my_fops = {
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .close = my_close,
};

printf("=== 设计模式 — 内核的智慧 ===\n\n");

printf("--- 1. 链表模式 ---\n");
printf("list_head:\n");
printf("  嵌入到结构体中\n");
printf("  list_add, list_del\n");
printf("  list_for_each_entry\n\n");

printf("--- 2. 通知链模式 ---\n");
printf("notifier_chain:\n");
printf("  注册回调函数\n");
printf("  事件发生时通知\n\n");

printf("--- 3. 引用计数模式 ---\n");
struct kref kref;
kref_init(&kref);
printf("kref_init: refcount = %d\n", kref.refcount);
kref_get(&kref);
printf("kref_get: refcount = %d\n", kref.refcount);
kref_put(&kref, NULL);
printf("kref_put: refcount = %d\n", kref.refcount);

printf("\n--- 4. 回调函数模式 ---\n");
printf("file_operations:\n");
my_fops.open();
my_fops.read();
my_fops.write();
my_fops.close();

printf("\n--- 5. 工作队列模式 ---\n");
printf("workqueue:\n");
printf("  延迟执行\n");
printf("  进程上下文\n");
printf("  schedule_work()\n\n");

printf("--- 6. 状态机模式 ---\n");
printf("状态机:\n");
printf("  网络协议状态\n");
printf("  TCP: ESTABLISHED, TIME_WAIT\n");
printf("  文件系统状态\n\n");

printf("--- 设计原则 ---\n");
printf("1. 分离关注点\n");
printf("2. 接口与实现分离\n");
printf("3. 组合优于继承\n");
printf("4. 依赖倒置\n");

道藏笔记

内核启示

内核中有许多经典的设计模式。

设计模式:

  • 链表 — list_head 嵌入结构体
  • 通知链 — 事件通知机制
  • 工作队列 — 延迟执行
  • 状态机 — 协议状态管理
  • 引用计数 — kref 防止泄漏
  • 回调函数 — file_operations 策略模式

设计原则:

  • 分离关注点
  • 接口与实现分离
  • 组合优于继承

设计模式是智慧——解决常见问题的通用方案。


破关试炼

设计模式之试

设计模式一章中,把文件操作多态交给驱动实现的函数表叫什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道