linux内核优先级设置的触发方式和流程

目录

1. 优先级设置触发方式

1.1 用户态接口

1.2 触发流程概览

2. 源码分析:普通进程优先级设置

2.1 系统调用入口(setpriority)

2.2 设置 Nice 值(set_user_nice)

2.3 动态优先级计算(effective_prio)

3. 源码分析:实时进程优先级设置

3.1 系统调用入口(sched_setscheduler)

3.2 更新调度类(__setscheduler_class)

4. 调度队列更新流程

4.1 重新入队任务(enqueue_task)

4.2 触发重新调度(resched_curr)

5. 权限与安全性处理

5.1 能力检查(capable(CAP_SYS_NICE))

5.2 实时优先级限制

6. 流程图解

7. 总结


1. 优先级设置触发方式

1.1 用户态接口
  • 普通进程优先级(Nice值)

    • 命令nicerenice

    • 系统调用setpriority(PRIO_PROCESS, pid, nice_value)

  • 实时进程优先级

    • 系统调用sched_setscheduler(pid, policy, ¶m),其中 param.sched_priority 指定实时优先级。

1.2 触发流程概览
  1. 用户发起请求:通过系统调用或工具修改优先级。

  2. 内核验证权限:检查用户是否具备 CAP_SYS_NICE 能力。

  3. 转换优先级值:将用户输入的优先级转换为内核内部表示。

  4. 更新任务结构:修改 task_struct 中的优先级字段。

  5. 调度队列调整:若任务在运行队列中,更新其在队列中的位置。


2. 源码分析:普通进程优先级设置

2.1 系统调用入口(setpriority
  • 用户调用 setpriority:修改进程的 Nice 值。

  • 内核处理函数kernel/sys.c → sys_setpriority() → do_setpriority().

// kernel/sys.c
SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval) {
    return do_setpriority(which, who, niceval);
}

static int do_setpriority(int which, int who, int niceval) {
    // 1. 参数验证:niceval 必须在 [-20, 19] 范围内
    if (niceval < MIN_NICE || niceval > MAX_NICE)
        return -EINVAL;

    // 2. 权限检查:需要 CAP_SYS_NICE 或目标进程属于当前用户
    if (!capable(CAP_SYS_NICE)) {
        if (cred->euid != pcred->uid && cred->euid != pcred->suid)
            return -EPERM;
    }

    // 3. 设置静态优先级(static_prio)
    set_user_nice(p, niceval);
    return 0;
}
2.2 设置 Nice 值(set_user_nice
  • 函数路径kernel/sched/core.c → set_user_nice().

  • 作用:将用户态 Nice 值转换为内核静态优先级(static_prio),并更新动态优先级(prio)。

void set_user_nice(struct task_struct *p, long nice) {
    // 1. 转换 Nice 值到静态优先级(static_prio = 120 + nice)
    int new_static_prio = NICE_TO_PRIO(nice);
    p->static_prio = new_static_prio;

    // 2. 更新动态优先级(prio)
    p->prio = effective_prio(p);

    // 3. 若任务在运行队列中,触发调度队列调整
    if (task_queued(p)) {
        enqueue_task(rq, p, ENQUEUE_UPDATE);
        resched_curr(rq); // 可能触发重新调度
    }
}
2.3 动态优先级计算(effective_prio
  • 函数路径kernel/sched/core.c → effective_prio().

  • 逻辑:普通进程的动态优先级基于静态优先级和调度策略计算。

static int effective_prio(struct task_struct *p) {
    if (rt_task(p)) // 实时进程直接返回 rt_priority
        return p->prio;
    return normal_prio(p); // 普通进程:static_prio + 调度策略偏移
}

3. 源码分析:实时进程优先级设置

3.1 系统调用入口(sched_setscheduler
  • 用户调用 sched_setscheduler:设置实时调度策略(SCHED_FIFO/SCHED_RR)和优先级。

  • 内核处理函数kernel/sched/core.c → sched_setscheduler() → __sched_setscheduler().

SYSCALL_DEFINE3(sched_setscheduler, pid_t, pid, int, policy, struct sched_param __user *, param) {
    struct sched_param lp;
    copy_from_user(&lp, param, sizeof(struct sched_param));
    return __sched_setscheduler(p, policy, &lp, true);
}

static int __sched_setscheduler(...) {
    // 1. 验证实时优先级范围(0-99)
    if (rt_policy(policy) && (param->sched_priority < 1 || param->sched_priority > 99))
        return -EINVAL;

    // 2. 权限检查:需要 CAP_SYS_NICE
    if (!capable(CAP_SYS_NICE)) {
        if (cred->euid != pcred->uid && cred->euid != pcred->suid)
            return -EPERM;
    }

    // 3. 更新调度策略和优先级
    p->policy = policy;
    p->rt_priority = param->sched_priority;

    // 4. 更新调度类(如从 CFS 切换到 RT)
    __setscheduler_class(p, policy);

    // 5. 若任务在运行队列中,重新入队以应用新策略
    if (task_running(rq, p)) {
        dequeue_task(rq, p, DEQUEUE_SAVE);
        enqueue_task(rq, p, ENQUEUE_RESTORE | ENQUEUE_NOCLOCK);
        resched_curr(rq);
    }
}
3.2 更新调度类(__setscheduler_class
  • 函数路径kernel/sched/core.c → __setscheduler_class().

  • 逻辑:根据调度策略绑定对应的调度类(如 rt_sched_class)。

static void __setscheduler_class(struct task_struct *p, int policy) {
    switch (policy) {
        case SCHED_NORMAL:
        case SCHED_BATCH:
            p->sched_class = &fair_sched_class; // 绑定到 CFS
            break;
        case SCHED_FIFO:
        case SCHED_RR:
            p->sched_class = &rt_sched_class;   // 绑定到实时调度类
            break;
        // ...其他策略处理
    }
}

4. 调度队列更新流程

4.1 重新入队任务(enqueue_task

当优先级或调度策略变更时,任务会被移出队列并重新插入,确保调度器按新优先级排序。

// kernel/sched/core.c
void enqueue_task(struct rq *rq, struct task_struct *p, int flags) {
    p->sched_class->enqueue_task(rq, p, flags); // 调用调度类的入队方法
}

// 示例:CFS 的 enqueue_task_fair
static void enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) {
    enqueue_entity(cfs_rq, se, flags); // 插入红黑树(按 vruntime 排序)
    update_load_avg(cfs_rq, se, UPDATE_TG); // 更新负载统计
}
4.2 触发重新调度(resched_curr

若当前任务优先级降低或被更高优先级任务抢占,设置 TIF_NEED_RESCHED 标志,触发调度。

void resched_curr(struct rq *rq) {
    struct task_struct *curr = rq->curr;
    set_tsk_need_resched(curr); // 设置 TIF_NEED_RESCHED
    smp_send_reschedule(cpu);   // 发送重新调度 IPI(多核)
}

5. 权限与安全性处理

5.1 能力检查(capable(CAP_SYS_NICE)
  • 普通用户:只能修改自己进程的 Nice 值(范围受限),无法设置实时优先级。

  • Root 用户:拥有 CAP_SYS_NICE 能力,可修改任意进程的优先级。

5.2 实时优先级限制
  • 默认配置:非 Root 用户无法设置实时优先级(需通过 /etc/security/limits.conf 解除限制)。


6. 流程图解

plaintext

复制

用户态触发
    |
    v
系统调用入口(setpriority/sched_setscheduler)
    |
    v
参数验证(范围、权限)
    |
    v
转换优先级(Nice → static_prio,用户优先级 → rt_priority)
    |
    v
更新 task_struct(static_prio, rt_priority, sched_class)
    |
    v
重新入队任务(enqueue_task) → 触发重新调度(resched_curr)
    |
    v
调度器按新优先级选择任务执行

7. 总结

  1. 触发方式

    • 普通进程:通过 setpriority 修改 Nice 值(范围 -20~19)。

    • 实时进程:通过 sched_setscheduler 设置策略和优先级(0~99)。

  2. 内核流程

    • 权限验证:检查 CAP_SYS_NICE 能力。

    • 优先级转换:将用户输入转换为内核内部表示(如 static_prio = 120 + nice)。

    • 更新任务结构:修改 task_struct 的 static_priort_priority 和 sched_class

    • 调度队列调整:重新入队任务,确保调度器按新优先级排序。

  3. 源码关键函数

    • do_setpriority():处理普通进程优先级设置。

    • __sched_setscheduler():处理实时进程策略和优先级。

    • effective_prio():计算动态优先级。

    • enqueue_task():更新调度队列。

通过模块化的调度类设计(如 fair_sched_class 和 rt_sched_class),Linux 内核实现了对不同优先级任务的高效管理,兼顾实时性和公平性。

你可能感兴趣的:(linux)