1. 程式人生 > 其它 >Linux核心原始碼—程序排程(4.20.17)

Linux核心原始碼—程序排程(4.20.17)

sched_class

在 Linux 中有多種不同的排程策略,每一種排程策略都由不同的排程器類實現,在 sched_class 中定義了排程器需要實現的介面。

struct sched_class {
	const struct sched_class *next;

	void (*enqueue_task) (struct rq *rq, struct task_struct *p, int flags);  //入隊
	void (*dequeue_task) (struct rq *rq, struct task_struct *p, int flags);  //出隊
	void (*yield_task)   (struct rq *rq);
	bool (*yield_to_task)(struct rq *rq, struct task_struct *p, bool preempt);

	void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);

	/*
	 * It is the responsibility of the pick_next_task() method that will
	 * return the next task to call put_prev_task() on the @prev task or
	 * something equivalent.
	 *
	 * May return RETRY_TASK when it finds a higher prio class has runnable
	 * tasks.
	 */
	struct task_struct * (*pick_next_task)(struct rq *rq,
					       struct task_struct *prev,
					       struct rq_flags *rf);
	void (*put_prev_task)(struct rq *rq, struct task_struct *p);

#ifdef CONFIG_SMP
	int  (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);
	void (*migrate_task_rq)(struct task_struct *p, int new_cpu);

	void (*task_woken)(struct rq *this_rq, struct task_struct *task);

	void (*set_cpus_allowed)(struct task_struct *p,
				 const struct cpumask *newmask);

	void (*rq_online)(struct rq *rq);
	void (*rq_offline)(struct rq *rq);
#endif

	void (*set_curr_task)(struct rq *rq);
	void (*task_tick)(struct rq *rq, struct task_struct *p, int queued);
	void (*task_fork)(struct task_struct *p);
	void (*task_dead)(struct task_struct *p);

	/*
	 * The switched_from() call is allowed to drop rq->lock, therefore we
	 * cannot assume the switched_from/switched_to pair is serliazed by
	 * rq->lock. They are however serialized by p->pi_lock.
	 */
	void (*switched_from)(struct rq *this_rq, struct task_struct *task);
	void (*switched_to)  (struct rq *this_rq, struct task_struct *task);
	void (*prio_changed) (struct rq *this_rq, struct task_struct *task,
			      int oldprio);

	unsigned int (*get_rr_interval)(struct rq *rq,
					struct task_struct *task);

	void (*update_curr)(struct rq *rq);

#define TASK_SET_GROUP		0
#define TASK_MOVE_GROUP		1

#ifdef CONFIG_FAIR_GROUP_SCHED
	void (*task_change_group)(struct task_struct *p, int type);
#endif
};

上面涉及到的 rq 就是 runqueue,每個 cpu 都對應著一個 runqueue,用來維護在該 cpu 上執行的程序。如果我們去看 struct rq 的定義,可以看到它包含了 cfs_rq, rt_rq 和 dl_rq 這三種不同的 rq 供排程器使用

如果我們現在 fork() 了一個程序,就需要將它加入到 cfs_rq 佇列中,此時 enqueue_task 執行 cfs 排程策略的入隊方法,也就是在紅黑樹中插入節點。

struct rq {
        ...
	struct cfs_rq		cfs;
	struct rt_rq		rt;
	struct dl_rq		dl;
        ...    
};

  

sched_entity

由於排程往往還需要一些額外的資訊,所以在 Linux 中定義了排程實體類。

struct sched_entity {
	/* For load-balancing: */
	struct load_weight		load;
	unsigned long			runnable_weight;
	struct rb_node			run_node;
	struct list_head		group_node;
	unsigned int			on_rq;

	u64				exec_start;
	u64				sum_exec_runtime;
	u64				vruntime;
	u64				prev_sum_exec_runtime;

	u64				nr_migrations;

	struct sched_statistics		statistics;

#ifdef CONFIG_FAIR_GROUP_SCHED
	int				depth;
	struct sched_entity		*parent;
	/* rq on which this entity is (to be) queued: */
	struct cfs_rq			*cfs_rq;
	/* rq "owned" by this entity/group: */
	struct cfs_rq			*my_q;
#endif
};

我們還可以看到,在一個程序描述符 task_struct 中是包含多個排程實體的,sched_class 指標指向其對應的排程器,然後排程器再排程相應的排程實體。

struct task_struct {
       ...

	int				on_rq;  //是否在就緒佇列上

	int				prio;
	int				static_prio;
	int				normal_prio;
	unsigned int			rt_priority;

	const struct sched_class	*sched_class;  //排程器
	struct sched_entity		se;        //cfs排程實體
	struct sched_rt_entity		rt;    //real_time排程實體
#ifdef CONFIG_CGROUP_SCHED
	struct task_group		*sched_task_group;
#endif
	struct sched_dl_entity		dl;    //deadline排程實體
           
        ...
};