• Linux 进程管理 实时调度类及SMP和NUMA



    在这里插入图片描述

    一、 实时调度类分析

    1.1 实时调度实体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_GROUP_SCHED
    	struct sched_rt_entity		*parent; // 指向父RT调度实体
    	/* rq on which this entity is (to be) queued: */
    	// rt_rq:实时类
    	struct rt_rq			*rt_rq;  // RT调度实体所属实时运行队列,被调度
    	/* rq "owned" by this entity/group: */
    	struct rt_rq			*my_q;  // RT调度实体拥有的实时调度队列,用于管理子任务或子组任务
    #endif
    } __randomize_layout;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    1.2 实时调度类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, // 核心调度器,从就绪队列中选择一个执行
    	.put_prev_task		= put_prev_task_rt, // 当任务将要被调度出时执行 
    	.set_next_task          = set_next_task_rt,
    
    #ifdef CONFIG_SMP
    	.balance		= balance_rt,
    	.select_task_rq		= select_task_rq_rt,
    	.set_cpus_allowed       = set_cpus_allowed_common,
    	.rq_online              = rq_online_rt,
    	.rq_offline             = rq_offline_rt,
    	.task_woken		= task_woken_rt,
    	.switched_from		= switched_from_rt,
    #endif
    
    	.task_tick		= task_tick_rt,
    
    	.get_rr_interval	= get_rr_interval_rt,
    
    	.prio_changed		= prio_changed_rt,
    	.switched_to		= switched_to_rt,
    
    	.update_curr		= update_curr_rt,
    
    #ifdef CONFIG_UCLAMP_TASK
    	.uclamp_enabled		= 1,
    #endif
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    1.3 实时调度类功能函数

    • 插入进程:enqueue_task_rt(…) -> ,源码如下:
    /*
     * Adding/removing a task to/from a priority array:
     * 更新调度信息,将调度实体插入到相应优先级队列末尾
     */
    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, p) && p->nr_cpus_allowed > 1)
    		enqueue_pushable_task(rq, p);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 选择进程: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 idx;
    	// 首先找到一个可用实体
    	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; // 返回找到的运行实体
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 删除进程: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表中进行删除
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    二、SMP和NUMA

    2.1 SMP(多对称处理器结构,UMA)

    在这里插入图片描述
    实践证明SMP服务器CPU利用率最高是2-4个CPU
    在这里插入图片描述

    2.2 NUMA(非一致内存访问结构)

    在这里插入图片描述

    2.3 CPU域初始化

    在这里插入图片描述

    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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    面渣逆袭:Redis连环五十二问,图文详解,这下面试稳了!
    【微服务】SpringCloud微服务剔除下线源码解析
    这两款简洁好看的软件你确定不想要吗
    Flink JobManager 内存占用大 问题
    猿创征文|我的半年算法学习成长之路~
    selenium-webdriver-Chrome新驱动地址(Chrome115及以上版本)
    R语言获取data.table分组下每个分组的第一条数据
    JAVA非递归遍历二叉树
    Java配置39-搭建ELK服务器
    如何使用Map处理Dom节点
  • 原文地址:https://blog.csdn.net/SGchi/article/details/134475854