分析进程调度时机
不同类型的进程有不同的调度需求
第一种分类:
I/O密集型,等待I/O的时间用来处理其他进程
CPU密集型
第二种分类
批处理进程:不必与用户交互,不用很快响应
实时进程:有实习需求,响应时间要短、要稳定
交互式进程:需要经常与用户交互,需要较长时间等待用户输入,响应时间要快,比如shell
linux支持普通的分时系统和实时系统
linux的调度基于分时和优先级。优先级是动态的,可以用设置nice。
进程调度通过schedule函数,进程调度的时机
1.中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
2.内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
3.用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度
中断保存上下文和进程保存上下文不同
中断:保存现场,恢复现场;在同一个进程中
进程:通过switch to的机制汇编实现;两个进程的切换
进程上下文包括:用户地址空间(程序代码、数据,用户堆栈等),控制信息(进程描述符、内核堆栈等),硬件上下文
进程调度通过schedule函数,下面分析schedule函数的执行过程
1 2865asmlinkage __visible void __sched schedule(void)//visible,表示在内核的部分都可以看到这个函数 2 2866{ 3 2867 struct task_struct *tsk = current; 4 2868 5 2869 sched_submit_work(tsk); 6 2870 __schedule(); 7 2871}
调用__schedule
1 static void __sched __schedule(void) 2 2771{ 3 2772 struct task_struct *prev, *next; 4 2773 unsigned long *switch_count; 5 2774 struct rq *rq; 6 2775 int cpu; 7 2776 8 2777need_resched: 9 2778 preempt_disable(); 10 2779 cpu = smp_processor_id(); 11 2780 rq = cpu_rq(cpu); 12 2781 rcu_note_context_switch(cpu); 13 2782 prev = rq->curr; 14 2783 15 2784 schedule_debug(prev); 16 2785 17 2786 if (sched_feat(HRTICK)) 18 2787 hrtick_clear(rq); 19 2788 20 2789 /* 21 2790 * Make sure that signal_pending_state()->signal_pending() below 22 2791 * can't be reordered with __set_current_state(TASK_INTERRUPTIBLE) 23 2792 * done by the caller to avoid the race with signal_wake_up(). 24 2793 */ 25 2794 smp_mb__before_spinlock(); 26 2795 raw_spin_lock_irq(&rq->lock); 27 2796 28 2797 switch_count = &prev->nivcsw; 29 2798 if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) { 30 2799 if (unlikely(signal_pending_state(prev->state, prev))) { 31 2800 prev->state = TASK_RUNNING; 32 2801 } else { 33 2802 deactivate_task(rq, prev, DEQUEUE_SLEEP); 34 2803 prev->on_rq = 0; 35 2804 36 2805 /* 37 2806 * If a worker went to sleep, notify and ask workqueue 38 2807 * whether it wants to wake up a task to maintain 39 2808 * concurrency. 40 2809 */ 41 2810 if (prev->flags & PF_WQ_WORKER) { 42 2811 struct task_struct *to_wakeup; 43 2812 44 2813 to_wakeup = wq_worker_sleeping(prev, cpu); 45 2814 if (to_wakeup) 46 2815 try_to_wake_up_local(to_wakeup); 47 2816 } 48 2817 } 49 2818 switch_count = &prev->nvcsw; 50 2819 } 51 2820 52 2821 if (task_on_rq_queued(prev) || rq->skip_clock_update < 0) 53 2822 update_rq_clock(rq); 54 2823 55 2824 next = pick_next_task(rq, prev);//选择下一个进程 56 2825 clear_tsk_need_resched(prev); 57 2826 clear_preempt_need_resched(); 58 2827 rq->skip_clock_update = 0; 59 2828 60 2829 if (likely(prev != next)) { 61 2830 rq->nr_switches++; 62 2831 rq->curr = next; 63 2832 ++*switch_count; 64 2833 65 2834 context_switch(rq, prev, next); /* unlocks the rq */ 66 2835 /* 67 2836 * The context switch have flipped the stack from under us 68 2837 * and restored the local variables which were saved when 69 2838 * this task called schedule() in the past. prev == current 70 2839 * is still correct, but it can be moved to another cpu/rq. 71 2840 */ 72 2841 cpu = smp_processor_id(); 73 2842 rq = cpu_rq(cpu); 74 2843 } else 75 2844 raw_spin_unlock_irq(&rq->lock); 76 2845 77 2846 post_schedule(rq); 78 2847 79 2848 sched_preempt_enable_no_resched(); 80 2849 if (need_resched()) 81 2850 goto need_resched; 82 2851}
next = pick_next_task(rq, prev)指明下一个进程,其中的调度策略被封装了
context_switch(rq, prev, next)用于完成上下文的切换
实验:函数进程调度分析调试
使用gdb调试,设置断点__schedule,context_switch,__switch_to
执行到__schedule,根据next表示的进程,进行进程调用,通过context_switch切换上下文
switch_to函数切换堆栈等