Linux内核深度解析
上QQ阅读APP看书,第一时间看更新

2.9.6 迁移线程

每个处理器有一个迁移线程,线程名称是“migration/<cpu_id>”,属于停机调度类,可以抢占所有其他进程,其他进程不可以抢占它。迁移线程有两个作用。

(1)调度器发出迁移请求,迁移线程处理迁移请求,把进程迁移到目标处理器。

(2)执行主动负载均衡。

如图2.47所示,每个处理器有一个停机工作管理器,成员thread指向迁移线程的进程描述符,成员works是停机工作队列的头节点,每个节点是一个停机工作,数据类型是结构体cpu_stop_work。内核提供了两个添加停机工作的函数。

图2.47 停机工作管理器的数据结构

(1)stop_one_cpu用来向指定处理器添加停机工作,并且等待停机工作完成。

    int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg);

(2)stop_one_cpu_nowait用来向指定处理器添加停机工作,但是不等待停机工作完成。

    bool stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg, struct cpu_
    stop_work *work_buf);

如图2.48所示,迁移线程的线程函数是smpboot_thread_fn,如果当前处理器的停机工作队列不是空的,重复执行下面的步骤。

图2.48 迁移线程的执行流程

(1)从停机工作队列中取一个工作。

(2)执行工作函数。

(3)如果发起请求的进程正在等待,那么发送处理完成的通知。

如图2.49所示,调用系统调用sched_setaffinity以设置进程的处理器亲和性时,如果进程正在执行或者被唤醒,假设进程在处理器n上,调度器就会向处理器n的迁移线程发出迁移请求:“向处理器n的停机工作队列添加一个工作,工作函数是migration_cpu_stop”,然后唤醒处理器n的迁移线程,等待迁移线程处理完迁移请求。

图2.49 设置处理器亲和性时发出迁移请求

函数migration_cpu_stop负责把进程从当前处理器迁移到目标处理器,参数的类型是结构体migration_arg,成员task是需要迁移的进程,成员dest_cpu是目标处理器,其代码如下:

    kernel/sched/core.c
    1   static int migration_cpu_stop(void *data)
    2   {
    3    struct migration_arg *arg = data;
    4    struct task_struct *p = arg->task;
    5    struct rq *rq = this_rq();
    6    struct rq_flags rf;
    7
    8    local_irq_disable();
    9    sched_ttwu_pending();
    10
    11   raw_spin_lock(&p->pi_lock);
    12   rq_lock(rq, &rf);
    13   if (task_rq(p) == rq) {
    14        if (task_on_rq_queued(p))
    15              rq = __migrate_task(rq, &rf, p, arg->dest_cpu);
    16        else
    17              p->wake_cpu = arg->dest_cpu;
    18   }
    19   rq_unlock(rq, &rf);
    20   raw_spin_unlock(&p->pi_lock);
    21
    22   local_irq_enable();
    23   return 0;
    24  }

第13行代码,检查进程p是否在当前处理器上。

第14行和第15行代码,如果进程p在当前处理器的运行队列中,那么把进程p迁移到目标处理器,从当前处理器的运行队中列删除,添加到目标处理器的运行队列中。

第16行和第17行代码,如果进程p正在睡眠,那么使用进程描述符的成员wake_cpu记录目标处理器,等到唤醒进程p的时候迁移到目标处理器。

公平调度类执行处理器负载均衡失败的时候,为最忙处理器设置主动负载均衡标志,唤醒最忙处理器的迁移线程。函数active_load_balance_cpu_stop负责执行主动负载均衡,执行流程如图2.50所示,先判断运行队列是否设置了主动负载均衡标志,如果设置了,那么从当前处理器的运行队列中选择一个公平调度类的进程,清除运行队列的主动负载均衡标志,把进程迁移到目标处理器。

图2.50 主动负载均衡的执行流程