@linux内核源码的个人理解
首先,我简单的介绍一下SET_USER_NICE
源码的大致功能:
就简单地从字面意思上看,SET
:设置、USER
:用户、NICE
:优先级的值。所以SET_USER_NICE
连起来就是设置用户的优先级,也就是设置进程的优先级。
NICE
是反应进程优先级的一个值,范围是[-19,+20],一共有40个值,值越小,代表的优先级更高;反之,值越大,优先级越低。
SET_USER_NICE
源码链接:https://elixir.bootlin.com/linux/latest/source/kernel/sched/core.c#L3894
// ./kernel/sched/core.c
void set_user_nice(struct task_struct *p, long nice)
{
bool queued, running;//布尔值 排队状态或运行状态
int old_prio, delta;//原优先级,参数
struct rq_flags rf;//运行队列的标志
struct rq *rq;//运行队列
if (task_nice(p) == nice || nice < MIN_NICE || nice > MAX_NICE)//nice -20~19
return;
rq = task_rq_lock(p, &rf);//上锁
update_rq_clock(rq);//更新运行队列时钟
if (task_has_dl_policy(p) || task_has_rt_policy(p)) {//实时进程
p->static_prio = NICE_TO_PRIO(nice);
goto out_unlock;//解锁
}
queued = task_on_rq_queued(p);//排队状态
running = task_current(rq, p);//运行状态
if (queued)
dequeue_task(rq, p, DEQUEUE_SAVE | DEQUEUE_NOCLOCK);//dequeue_task 出队
if (running)
put_prev_task(rq, p);//用另一个进程代替当前运行的进程之前调用,将切换出去的进程插入到队尾
p->static_prio = NICE_TO_PRIO(nice);//将进程的静态优先级赋值
set_load_weight(p, true);//负责根据非实时进程类型极其静态优先级计算符合权重(cpu资源),CFS调度器在计算进程的虚拟运行时间或者调度延迟时都是使用的权重(cpu资源)。
old_prio = p->prio;//旧的优先级
p->prio = effective_prio(p);//返回进程p的动态优先级
delta = p->prio - old_prio;//动态优先级改变状况
if (queued) {
enqueue_task(rq, p, ENQUEUE_RESTORE | ENQUEUE_NOCLOCK);//enqueue_task将进程添加到具体的运行队列中
if (delta < 0 || (delta > 0 && task_running(rq, p)))
resched_curr(rq);//用于标记当前进程需要被调度出去
}
if (running)
set_curr_task(rq, p);//在当前进程的调度策略发生变化时调用,那么需要调用这个函数改变CPU的当前task
out_unlock:
task_rq_unlock(rq, p, &rf);//解锁
}
rq_flags
:
struct rq_flags {
unsigned long flags;
struct pin_cookie cookie;
#ifdef CONFIG_SCHED_DEBUG
/*
* A copy of (rq::clock_update_flags & RQCF_UPDATED) for the
* current pin context is stashed here in case it needs to be
* restored in rq_repin_lock().
*/
unsigned int clock_update_flags;
#endif
};
本来想纠结一下flags
与int clock_update_flags
的long
与int
的区别,查阅了一些资料之后发现并没有那么容易来区分,所以我选择忽略这个问题。
总之rq_flags
的作用就是一个运行队列的标志。
task_nice
:
static inline int task_nice(const struct task_struct *p)
{
return PRIO_TO_NICE((p)->static_prio);
}
返回PRIO_TO_NICE
PRIO_TO_NICE
:
#define PRIO_TO_NICE(prio) ((prio) - DEFAULT_PRIO)
priority值转换为nice值
NICE_TO_PRIO
:
#define NICE_TO_PRIO(nice) ((nice) + DEFAULT_PRIO)
nice值转换为priority值
关于nice值和prioirty值的详细讲解博客链接:http://blog.51cto.com/frankch/1773621
task_has_dl_policy
:
static inline int task_has_dl_policy(struct task_struct *p)
{
return dl_policy(p->policy);
}
dl_policy
:
static inline int dl_policy(int policy)
{
return policy == SCHED_DEADLINE;
}
SCHED_DEADLINE
:
#define SCHED_DEADLINE 6
task_has_rt_policy
:
static inline int task_has_rt_policy(struct task_struct *p)
{
return rt_policy(p->policy);
}
rt_policy
:
static inline int rt_policy(int policy)
{
return policy == SCHED_FIFO || policy == SCHED_RR;
}
SCHED_FIFO
:
#define SCHED_FIFO 1
SSCHED_RR
:
#define SCHED_RR 2
如果当前是实时进程,实时进程的的调度策略可以分为deadline
/fifo
/rr
。 deadline
最后期限/fifo
先来先服务/rr
时间片轮转。
DL调度器是按照任务的deadline
进行调度的。当产生一个调度点的时候,DL调度器总是选择其deadline
距离当前时间点最近的那个任务并调度它执行。
FIFO
(first-in-first-out)和RR
(round-robin)是RT调度器的两种调度策略。
实时进程的调度方式只有rt
和dl
。
其实如果是实时进程,针对实时进程设置nice
值其实是没有作用的,但是这里还是将nice
值转成优先级后设置到p->static_prio
。
详解见以下链接:http://www.wowotech.net/process_management/deadline-scheduler-1.html (deadline调度器之(一):原理)
有必要说一下静态优先级、动态优先级、实时优先级
静态优先级:
定义:不随时间改变,内核不会主动修改它,只能通过系统调用nice
去修改static_prio
,也就是我们能改的优先级。
动态优先级:
定义:调度程序通过或减少进程静态优先级来奖励IO消耗型进程或惩罚CPU消耗进程,调整后的优先级为动态优先级(prio
)。
实时优先级:
实时优先级只对实时进程有效,所以上面对实时进程不做处理。
详解见以下链接:https://www.cnblogs.com/zengkefu/p/5566368.html
task_on_rq_queued
:
static inline int task_on_rq_queued(struct task_struct *p)
{
return p->on_rq == TASK_ON_RQ_QUEUED;
}
TASK_ON_RQ_QUEUED
:
#define TASK_ON_RQ_QUEUED 1
task_current
:
static inline int task_current(struct rq *rq, struct task_struct *p)
{
return rq->curr == p;
}
enqueue_task
将进程添加到具体的运行队列中,dequeue_task
相反,出队。
dequeue_task
:
static inline void dequeue_task(struct rq *rq, struct task_struct *p, int flags)
{
if (!(flags & DEQUEUE_NOCLOCK))
update_rq_clock(rq);
if (!(flags & DEQUEUE_SAVE)) {
sched_info_dequeued(rq, p);
psi_dequeue(p, flags & DEQUEUE_SLEEP);
}
p->sched_class->dequeue_task(rq, p, flags);
}
flags
:其实就是进程的状态标志,我没有深究。
put_prev_task
:
static inline void put_prev_task(struct rq *rq, struct task_struct *prev)
{
prev->sched_class->put_prev_task(rq, prev);
}
将当前进程,也就是被切换出去的进程重新插入到各自的运行队列中,对于CFS算法插入到红黑树的合适位置上,对于实时调度插入到同一个优先级队列的链表尾部。
通知调度器类当前运行的进程将要被另一个进程代替,用另一个进程代替当前运行的进程之前调用,将切换出去的进程插入到队尾。
effective_prio
:
static int effective_prio(struct task_struct *p)
{
p->normal_prio = normal_prio(p);
/*
* If we are RT tasks or we were boosted to RT priority,
* keep the priority unchanged. Otherwise, update priority
* to the normal priority:
*/
if (!rt_prio(p->prio))
return p->normal_prio;
return p->prio;
}
返回进程的prio
值。
如果是实时进程,那么直接返回该实时进程的prio
。
normal_prio
:
static inline int normal_prio(struct task_struct *p)
{
int prio;
if (task_has_dl_policy(p))
prio = MAX_DL_PRIO-1;
else if (task_has_rt_policy(p))
prio = MAX_RT_PRIO-1 - p->rt_priority;
else
prio = __normal_prio(p);
return prio;
}
resched_curr
:
void resched_curr(struct rq *rq)
{
struct task_struct *curr = rq->curr;
int cpu;
lockdep_assert_held(&rq->lock);
if (test_tsk_need_resched(curr))
return;
cpu = cpu_of(rq);
if (cpu == smp_processor_id()) {
set_tsk_need_resched(curr);
set_preempt_need_resched();
return;
}
if (set_nr_and_not_polling(curr))
smp_send_reschedule(cpu);
else
trace_sched_wake_idle_without_ipi(cpu);
}
函数用于标记当前进程需要被调度出去 。
set_curr_task
:
static inline void set_curr_task(struct rq *rq, struct task_struct *curr)
{
curr->sched_class->set_curr_task(rq);
}
在当前进程的调度策略发生变化时调用,那么需要调用这个函数改变CPU的当前task。
但是不一定是调度策略发生变化就要改变当前的进程,这得说清楚。调度策略变化不一定会引起当前进程的变化。
这篇文章其实相当于做了个粗略的笔记,还存在一定的理解不当之处,望读者见谅.
ps:估计也不会有读者(>_<)