IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux调度(smp)函数分析 -> 正文阅读

[系统运维]linux调度(smp)函数分析

sched_init

void __init sched_init(void)
{
        unsigned long ptr = 0;
        int i;

        /* Make sure the linker didn't screw up */
        BUG_ON(&idle_sched_class + 1 != &fair_sched_class ||
               &fair_sched_class + 1 != &rt_sched_class ||
               &rt_sched_class + 1   != &dl_sched_class);
#ifdef CONFIG_SMP
        BUG_ON(&dl_sched_class + 1 != &stop_sched_class);
#endif

        wait_bit_init();
		// 初始化等待队列

	...

	init_rt_bandwidth(&def_rt_bandwidth, global_rt_period(), global_rt_runtime());
	// 初始化(控制全局的)rt使用带宽
    init_dl_bandwidth(&def_dl_bandwidth, global_rt_period(), global_rt_runtime());
    // 初始化(控制全局的)dl使用带宽

 	...

	for_each_possible_cpu(i) {
	// 遍历所有可用的cpu,为每个cpu初始化相关结构
	
       struct rq *rq;
       // 每个CPU的主要运行队列数据结构

       rq = cpu_rq(i);
       raw_spin_lock_init(&rq->lock);
       rq->nr_running = 0;
       rq->calc_load_active = 0;
       rq->calc_load_update = jiffies + LOAD_FREQ;
       init_cfs_rq(&rq->cfs);
       // 初始化cfs运行队列
       
       init_rt_rq(&rq->rt);
	   // 初始化rt运行队列(Real-Time class)
	   
       init_dl_rq(&rq->dl);
	   // 初始化dl运行队列(Deadline class)
	   
	...
	
		rq->rt.rt_runtime = def_rt_bandwidth.rt_runtime;
		// global_rt_runtime(0.95s)

		hrtick_rq_init(rq);
		// 内核定时器初始化(高分辨率定时器时间片,在禁用中断的情况下从hardirq上下文运行)
                atomic_set(&rq->nr_iowait, 0);
     }

	 set_load_weight(&init_task, false);
	 // 设置init_task权重

	mmgrab(&init_mm);
	// init_mm即使在完成所属任务后也不会被释放,并且之前必须使用mmget_not_zero访问它
	
    enter_lazy_tlb(&init_mm, current);
    // init_mm切换到内核线程,enter_lazy_tlb是来自调度程序的提示,表示我们正在输入内核线程或其他没有mm的上下文

	init_idle(current, smp_processor_id());
	// 为给定的CPU设置一个空闲线程,从技术上讲,schedule()不应该从这个线程(current)调用,当此运行队列变为“空闲”时,我们只是重新开始运行
	
    calc_load_update = jiffies + LOAD_FREQ;

	...

	psi_init();
	// 初始化统计管理结构

	init_uclamp();
	// 初始化uclamp

    scheduler_running = 1;
    
}

? sched_init函数用于初始化每个可用的cpu队列,每个CPU维护自己的rq数据结构,CPU之间使用ipi通信(SMP模式下)。


schedule

asmlinkage __visible void __sched schedule(void)
{
        struct task_struct *tsk = current;

        sched_submit_work(tsk);

? schedule函数用于强制申请进程调度,进入sched_submit_work(tsk)函数:

static inline void sched_submit_work(struct task_struct *tsk)
{
        if (!tsk->state) //如果当前进程为空闲状态,直接返回
                return;

        /*
         * If a worker went to sleep, notify and ask workqueue whether
         * it wants to wake up a task to maintain concurrency.
         * As this function is called inside the schedule() context,
         * we disable preemption to avoid it calling schedule() again
         * in the possible wakeup of a kworker and because wq_worker_sleeping()
         * requires it.
         */
        if (tsk->flags & (PF_WQ_WORKER | PF_IO_WORKER)) { // 如果当前进程有工作队列任务或IO队列任务
                preempt_disable(); // 内核抢占计数加1,防止被(调度)执行
                if (tsk->flags & PF_WQ_WORKER)
                        wq_worker_sleeping(tsk); // 如果工作队列任务没有运行或者工作队列任务没有任务,直接返回,或者唤醒一个空闲的进程(工作池为运行状态并且列表不为空)
                else
                        io_wq_worker_sleeping(tsk); //如果IO队列任务处于活跃状态或运行状态,直接返回,或者唤醒管理者创建一个任务
                preempt_enable_no_resched(); // 内核抢占计数减1,但不立即抢占式调度
        }

        if (tsk_is_pi_blocked(tsk)) // 检测tsk的死锁检测器是否为空,不为空直接返回
                return;

		/*
         * If we are going to sleep and we have plugged IO queued,
         * make sure to submit it to avoid deadlocks.
         */
        if (blk_needs_flush_plug(tsk))
                blk_schedule_flush_plug(tsk); // 异步执行unplug,最终定时器定时触发kblockd将request下发给驱动程序
}

? sched_submit_work用于处理/提交当前进程的队列进展,防止后续出现死锁情况,返回schedule函数:

  do {
                preempt_disable(); // 内核抢占计数加1,防止被(调度)执行
                __schedule(false);

? 进入__schedule(false)函数:

static void __sched notrace __schedule(bool preempt)
{
        struct task_struct *prev, *next;
        unsigned long *switch_count;
        unsigned long prev_state;
        struct rq_flags rf;
        struct rq *rq;
        int cpu;

        cpu = smp_processor_id(); // 获取当前的cpu id
        rq = cpu_rq(cpu); // 获取rq数据结构
        prev = rq->curr; // 当前cpu标记为prev

        schedule_debug(prev, preempt);

        if (sched_feat(HRTICK))
                hrtick_clear(rq);

        local_irq_disable(); //  设置当前CPU的中断屏蔽位
        rcu_note_context_switch(preempt); // 更新rcu状态,记录本cpu经过qs

		...

		rq_lock(rq, &rf); //为rq加锁
        smp_mb__after_spinlock(); 

        /* Promote REQ to ACT */
        rq->clock_update_flags <<= 1;
        update_rq_clock(rq);

        switch_count = &prev->nivcsw; 

		 prev_state = prev->state;  // 获得前一个任务状态
        if (!preempt && prev_state) { 
                if (signal_pending_state(prev_state, prev)) { // 如果任务有TASK_INTERRUPTIBLE标识或者挂起为真
                        prev->state = TASK_RUNNING; 
                } else {
						...
						
						deactivate_task(rq, prev, DEQUEUE_SLEEP | DEQUEUE_NOCLOCK); // cpu的队列中删除任务
						
						...
			 }

			 next = pick_next_task(rq, prev, &rf); // 获取下一个的进程(优先级相等)
			 clear_tsk_need_resched(prev); // 清理上一个进程的TIF_NEED_RESCHED标志
        	 clear_preempt_need_resched(); // PREEMPT_OFFSET | PREEMPT_NEED_RESCHED

			 if (likely(prev != next)) {
                rq->nr_switches++;
                /*
                 * RCU users of rcu_dereference(rq->curr) may not see
                 * changes to task_struct made by pick_next_task().
                 */
                RCU_INIT_POINTER(rq->curr, next); // cpu的rq结构当前(指向)为下一个进程

				 ++*switch_count;

                psi_sched_switch(prev, next, !task_on_rq_queued(prev)); // 统计调度为下一个进程

                trace_sched_switch(preempt, prev, next); // 捕获下一个进程

				/* Also unlocks the rq: */
                rq = context_switch(rq, prev, next, &rf); // 从当前进程切换到下一个进程
        } else {
                rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
                rq_unlock_irq(rq, &rf);
        }

        balance_callback(rq); // head = rq->balance_callback; next = head->next; ? rq->balance_callback = rq->balance_callback->next;

? __schedule函数实现了当前进程到下一个进程的调转,及当前进程的状态保存,回到schedule函数:

                sched_preempt_enable_no_resched();  // 内核抢占计数减1,但不立即抢占式调度
        } while (need_resched()); // 如果thread_info->flags包含TIF_NEED_RESCHED标志,表示需要执行调度
        sched_update_worker(tsk); // 恢复之前睡眠的队列任务
}

? schedule调用后至少执行一次__schedule函数,之后通过TIF_NEED_RESCHED标志确定是否调度。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 18:25:00  更:2022-04-18 18:25:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 22:21:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码