4. kthread_create() 源码分析:创建过程的核心路径、kthreadd内核线程的职责、wait_for_completion()同步机制

内核线程的创建并非一蹴而就,其背后涉及一套精密的协作机制。本章将深入剖析 kthread_create() 函数的源码执行路径,揭示 kthreadd 内核线程作为“线程保姆”的核心职责,并详细解读 wait_for_completion() 同步原语如何确保创建过程的原子性与正确性。

4.1 kthread_create() 核心路径解析

kthread_create() 是一个宏定义,最终会调用 __kthread_create_on_node() 函数。其核心思想是:创建一个特殊的任务(task),该任务会立即进入睡眠状态,等待 kthreadd 线程将其唤醒并执行真正的线程函数。

关键路径: kthread_create() → kthread_create_on_node() → __kthread_create_on_node() → 创建新 task → 唤醒 kthreadd → kthreadd 执行 create_kthread() → 完成创建。

4.1.1 核心数据结构:struct kthread_create_info

在创建过程中,内核使用 struct kthread_create_info 结构体来传递参数和同步状态。该结构体是 kthreadd 与创建者线程之间的通信契约。

struct kthread_create_info {
    /* 线程函数的参数 */
    int (*threadfn)(void *data);
    void *data;
    /* 存放创建好的 task_struct 指针 */
    struct task_struct *result;
    /* 完成变量,用于同步 */
    struct completion done;
    /* 创建完成后的状态 */
    int status;
};

4.1.2 创建流程步骤

  1. 分配与初始化: 调用 kmalloc() 分配 kthread_create_info 结构体,并填充线程函数 threadfn 和参数 data。
  2. 创建内核线程: 调用 kthreadd() 或 kernel_thread() 创建一个新的内核线程。这个新线程的入口函数是 kthread(),而不是用户指定的 threadfn。
  3. 挂起新线程: 新创建的线程在 kthread() 函数中立即执行 __kthread_create_on_node() 中的 wait_for_completion(&create->done),使自己进入不可中断的睡眠状态(TASK_UNINTERRUPTIBLE)。
  4. 唤醒 kthreadd: 创建者线程将 kthread_create_info 结构体加入全局链表,并唤醒 kthreadd 内核线程。
  5. kthreadd 处理: kthreadd 被唤醒后,遍历链表,对每个待创建的线程调用 create_kthread()。
  6. 完成创建: create_kthread() 调用 kernel_thread() 创建真正的线程,并设置其入口为 kthread()。随后,kthreadd 通过 complete() 唤醒之前挂起的线程,使其继续执行。
设计意图: 这种“两步走”的设计将线程的创建与初始化分离。创建者线程可以快速返回,而繁重的初始化工作由 kthreadd 在后台完成,提高了系统的响应性。
kthread_create() 核心路径流程图 调用 kthread_create() 分配 kthread_create_info 创建新 task (入口 kthread) 新 task 挂起等待完成 唤醒 kthreadd kthreadd 调用 create_kthread() complete() 唤醒新线程

4.2 kthreadd 内核线程的职责

kthreadd(PID=2)是所有内核线程的“守护进程”。它运行在核心态,负责监听创建请求并执行实际的线程创建工作。没有 kthreadd,内核线程的创建将无法完成。

4.2.1 kthreadd 的核心工作循环

kthreadd 的主函数是一个无限循环,其核心逻辑如下:

for (;;) {
    /* 1. 将当前线程状态设置为 TASK_INTERRUPTIBLE */
    set_current_state(TASK_INTERRUPTIBLE);
    
    /* 2. 检查全局链表 kthread_create_list 是否为空 */
    if (list_empty(&kthread_create_list))
        schedule();  /* 如果没有待创建任务,则睡眠 */
    
    /* 3. 被唤醒后,将状态设为 TASK_RUNNING */
    __set_current_state(TASK_RUNNING);
    
    /* 4. 遍历链表,处理所有待创建的线程 */
    while (!list_empty(&kthread_create_list)) {
        struct kthread_create_info *create;
        
        create = list_entry(kthread_create_list.next,
                            struct kthread_create_info, list);
        list_del_init(&create->list);
        
        /* 5. 调用 create_kthread() 完成实际创建 */
        create_kthread(create);
    }
}

4.2.2 kthreadd 的职责清单

职责 说明 关键函数
监听创建请求 持续检查 kthread_create_list 链表,等待新的创建请求。 schedule()
执行线程创建 调用 kernel_thread() 创建新的内核线程,并设置其入口函数为 kthread()。 create_kthread()
同步与通知 通过 complete() 唤醒等待中的创建者线程,告知创建完成。 complete()
资源管理 负责释放 kthread_create_info 结构体,避免内存泄漏。 kfree()
注意事项: kthreadd 是一个极其重要的内核线程,如果它被意外杀死或阻塞,整个系统的内核线程创建功能将瘫痪,导致系统无法正常工作。因此,kthreadd 的代码路径必须保持高效和稳定。
kthreadd 工作循环与职责 kthreadd 主循环 监听创建请求 执行线程创建 同步与通知 资源管理

4.3 wait_for_completion() 同步机制

wait_for_completion() 是 Linux 内核中一种轻量级的同步原语,用于等待一个事件的发生。在 kthread_create() 的上下文中,它被用来确保创建者线程等待 kthreadd 完成新线程的初始化工作。

4.3.1 核心数据结构:struct completion

struct completion {
    unsigned int done;          /* 完成状态:0 表示未完成,非0表示已完成 */
    wait_queue_head_t wait;     /* 等待队列,存放所有等待该完成的进程 */
};

4.3.2 工作流程

  1. 初始化: 通过 init_completion() 或 DECLARE_COMPLETION() 将 done 字段初始化为 0。
  2. 等待: 调用 wait_for_completion() 的进程会检查 done 是否为 0。如果为 0,则将自己加入等待队列,并进入不可中断的睡眠状态(TASK_UNINTERRUPTIBLE)。
  3. 完成: 另一个进程(通常是 kthreadd)调用 complete(),将 done 加 1,并唤醒等待队列中的所有进程。
  4. 继续: 被唤醒的进程检查 done 是否为非 0,如果是,则退出等待,继续执行。

4.3.3 在 kthread_create 中的具体应用

在 __kthread_create_on_node() 函数中,创建者线程会执行以下代码:

/* 创建者线程 */
struct kthread_create_info *create = kmalloc(sizeof(*create), GFP_KERNEL);
init_completion(&create->done);
create->threadfn = threadfn;
create->data = data;

/* 将 create 加入全局链表,并唤醒 kthreadd */
spin_lock(&kthread_create_lock);
list_add_tail(&create->list, &kthread_create_list);
spin_unlock(&kthread_create_lock);
wake_up_process(kthreadd_task);

/* 等待 kthreadd 完成创建 */
wait_for_completion(&create->done);

/* 此时 create->result 中存放了新线程的 task_struct 指针 */
return create->result;

而 kthreadd 在 create_kthread() 中完成创建后,会调用:

/* kthreadd 线程 */
complete(&create->done);
为什么使用 TASK_UNINTERRUPTIBLE? 因为创建者线程必须等待新线程创建完成才能继续,如果使用可中断的睡眠(TASK_INTERRUPTIBLE),可能会被信号打断,导致创建过程不完整,引发资源泄漏或状态不一致。
wait_for_completion() 同步机制 创建者线程 wait_for_completion() 睡眠等待 (TASK_UNINTERRUPTIBLE) kthreadd 线程 complete() done++ 并唤醒等待队列 同步点

4.4 总结与对比

组件 角色 关键函数/机制 同步方式
kthread_create() 创建者接口 __kthread_create_on_node() 通过 wait_for_completion() 等待
kthreadd 线程守护进程 create_kthread() 通过 complete() 通知
wait_for_completion() 同步原语 complete() / wait_for_completion() 基于 done 计数器和等待队列

通过本章的分析,我们可以看到 kthread_create() 的创建过程是一个精心设计的协作流程。它利用 kthreadd 作为中央管理者,通过 wait_for_completion() 同步机制,确保了内核线程创建的原子性和可靠性。这种设计不仅提高了系统的响应性,还为后续的调度策略选型提供了稳定的基础。