linux内核源码的个人理解

@linux内核源码的个人理解

SET_USER_NICE

首先,我简单的介绍一下SET_USER_NICE源码的大致功能:

就简单地从字面意思上看,SET:设置、USER:用户、NICE:优先级的值。所以SET_USER_NICE连起来就是设置用户的优先级,也就是设置进程的优先级。

NICE是反应进程优先级的一个值,范围是[-19,+20],一共有40个值,值越小,代表的优先级更高;反之,值越大,优先级越低。

SET_USER_NICE源码及简略注释

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);//解锁
}

SET_USER_NICE中所用到的其他源码内容

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
};

本来想纠结一下flagsint clock_update_flagslongint的区别,查阅了一些资料之后发现并没有那么容易来区分,所以我选择忽略这个问题。

总之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/rrdeadline最后期限/fifo先来先服务/rr时间片轮转。

DL调度器是按照任务的deadline进行调度的。当产生一个调度点的时候,DL调度器总是选择其deadline距离当前时间点最近的那个任务并调度它执行。

FIFO(first-in-first-out)和RR(round-robin)是RT调度器的两种调度策略。

实时进程的调度方式只有rtdl

其实如果是实时进程,针对实时进程设置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:估计也不会有读者(>_<)

你可能感兴趣的:(简介)