Linux核心原始碼—程序排程(4.20.17)
阿新 • • 發佈:2022-03-07
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排程實體 ... };