实时调度类
- Linux 进程可分为两大类:实时进程和普通进程,实时进程与普通进程根本不同之处,如果系统中有一个实时进程且可执行,那么调度器总是会选择它,除非另有一个优先级更高实时进程。 SCHED_FIFO: 没有时间片,在被调度器选择之后,可以运行任意时长时间; SCHED_RR: 有时间片,其值在进程运行时会减少
实时调度实体sched_rt_entity 数据结构
表示实时调度实体,包含整个实时调度数据信息,
struct sched_rt_entity {
struct list_head run_list;//专门用于加入到优先级队列中
unsigned long timeout;//设置的时间超时
unsigned long watchdog_stamp; //用于记录jiffies值
unsigned int time_slice;//时间片
unsigned short on_rq;
unsigned short on_list;
struct sched_rt_entity *back; //临时用于从上往下链接RT调度实体使用
#ifdef CONFIG_RT_CONFIG_SCHED
struct sched_rt_entity *parent; //指向父RT调度实体
struct rt_rq *rt_rq;//RT调度实体所属的实时运行队列,被调度
struct rt_rq *my_q; //RT 调度实体所拥有的实时运行队列,用于管理任务或子组任务
#endif
} __randomize_layout;
实时调度类rt_sched_class 数据结构
const struct sched_class rt_sched_class = {
.next = &fair_sched_class,
.enqueue_task = enqueue_task_rt, //将一个task存放到就绪队列或者尾部
.dequeue_task = dequeue_task_rt, //将一个task从就绪队列尾部
.yield_task = yield_task_rt,//主动放弃执行
.check_preempt_curr = check_preempt_curr_rt,
.pick_next_task = pick_next_task_rt, //核心调度器选择就绪队列当中的哪个任务将要被调度,prev是将要被调度出的任务,返回值是将要被调度的任务。
.put_prev_task = put_prev_task_rt,//当一个任务将要被调度出时候执行
.set_next_task = set_next_task_rt,
}
解析一下rt_rq 源码
struct rt_rq {
struct rt_prio_array active; //优先级队列
unsigned int rt_nr_running;// 在rt 运行队列当中所有活动的任务数
unsigned int rr_nr_running;
#if defined CONFIG_SMP || defined CONFIG_RT_GROUP_SCHED
struct {
int curr; //当前 rt任务的最高优先级
#ifdef CONFIG_SMP
int next; //下一个要运行的RT任务优先级。如果两个任务都有最高优先级,cur==next
#endif
} highest_prio;
#endif
#ifdef CONFIG_SMP
unsigned long rt_nr_migratory;
unsigned long rt_nr_total;
int overloaded;
struct plist_head pushable_tasks;
#endif
int rt_queued;
int rt_throttled;
u64 rt_time;
u64 rt_runtime;
raw_spinlock_t rt_runtime_lock;
}
实时调度类操作核心函数enqueue_task_rt(...)->插入进程,具体Linux内核源码如下. 更新调度信息,将调度实体插入到相应优先级队列的末尾
static void enqueue_task_rt(struct rq *rq, struct task_struct *p, int flags)
{
struct sched_rt_entity *rt_se = &p->rt;
if(flags & ENQUEUE_WAKEUP)
rt_se -> timeout = 0;
enqueue_rt_entity(rt_se,flags);
if(!task_current(rq,q) && p->nr_cups_allowed > 1)
enqueue_pushable_task(rq,p);
}
实时调度类操作核心函数pick_next_rt_entity(...)->选择进程,实时调度会选择最高优先级的实时进程来运行:
static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,struct rt_rq *rt_rq)
{
struct rt_prio_array *array = &rt_rq->active;
struct sched_rt_entity *next = NULL;
struct list_head *queue;
int idex;
//第一个找到一个可用的实体
idx = sched_find_first_bit(array->bitmap);
BUG_ON(idx >= MAX_RT_PRIO);
//从链表组中找到对应的链表
queue = array->queue + idx;
next = list_entry(queue->next,struct sched_rt_entity,run_list);
return next; // 返回找到运行实体
}
实时调度类操作核心函数dequeue_task_rt(...)->删除进程,从优先级队列中删除实时进程,并更新调度信息,然后把这个进程添加队尾 删除进程
static void dequeue_task_rt(struct rq *rq,struct task_struct *p, int flags)
{
struct sched_rt_entity *rt_se = &p->rt;
update_curr_rt(rq); //更新调度数据信息等等
dequeue_rt_entity(rt_se,flags);//将rt_se 从运行队列当中删除,然后添加到队列尾部
dequeue_pushable_task(rq,p); //从hash 表当中进行删除
}
SMP和NUMA
SMP(对称多处理器结构)
对称多处理器结构(SMP),在对称多处理器系统中,所有处理器的地位都是平等的,所有的CPU共享全部资源,比如内存,总线,中断以及I/O系统等等,都具有相同的可访问性,消除结构上的障碍,最大特点就是共享所有资源。
在多处理器系统当中,内核必须考虑几个额外的问题,主要以确保良好的调度。
CPU负荷必须尽可能公平在所有处理器上共享。
进程与系统中某些处理器的亲和性必须是可设置的。
内核必须能够将进程从一个CPU迁移到另一个。
Linux SMP调度就是将进程安排/迁移到合适的CPU中去,保持各CPU负载均衡的过程。
当前使用的OTLP程序当中,用户访问一个中断数据库,如果采用SMP系统架构,它的效率要比MPP架构要快。
NUMA(非一致内存访问结构)
NUMA为是处理器计算机,系统各个CPU 都有本地内存,可以支持超快的访问能力,各个处理器之间通过总线连接起来,支持对其他CPU的本地内存访问(但比访问自己的内存要慢点)
一台物理服务器内部集成多个CPU,是系统具有较高事务处理能力,MUNA架构适合OLTP事务处理环境
CPU域初始化
根据实际物理属性,CPU分类(SMT,MC,SoC)Linux 内核分类(CONFIG_SCHED_SMT,CONFIG_SCHED_MC,DIE),Linux 内核对CPU 的管理是通过bitmap来管理,并且定义4种状态possible/present/online/active
```
extern struct cpumask __cpu_possible_mask;
extern struct cpumask __cpu_online_mask;
extern struct cpumask __cpu_present_mask;
extern struct cpumask __cpu_active_mask;
// 表示系统当中有多少个可以执行的CPU核心
#define cpu_possible_mask((const struct cpumask *) &__cpu_possible_mask)
//表示系统当中有多少个正在运行状态的CPU 核心
#define cpu_online_mask((const struct cpumask *) &__cpu_online_mask)
// 表示系统当中有多个具备online条件的CPU核心,他们不一定都处于online,有的cpu核心可能被热插拔
#define cpu_present_mask((const struct cpumask *) &__cpu_present_mask)
//表示系统当中有多个少活跃的CPU核心
#define cpu_active_mask((const struct cpumask *) &__cpu_active_mask)
```