Linux进程调度和切换过程分析
2013-05-10 22:13
405 查看
schedule()开始,说明几种不同类型的进程之间的调度选择;在相同类型的进程之间的调度选择算法。
在schedule()函数中,
首先禁止抢占,获取当前CPU,该CPU的执行队列,队列上正在执行的进程,以及该进程的交换计数信息并释放该进程占用的锁。之后,对禁止中断,更新运行队列时钟,该队列的自旋时钟加锁,后清除当前进程的thread_flag中TIF_NEED_RESCHED,
如果进程不在可运行状态,并且可被抢占,若进程处于非阻塞挂起,则将其改为可运行,否则调用deactivate_task()函数,并修改上下文交换次数。其中在deactive_task()函数中调用了denqueue_task()函数:
P进程调用属于自己调度类的dequeue_task()方法,将p从当前rp运行队列上移出。例如对于公平调度队列中的进程调用以下函数:
对p的所有实体除含有子实体的父进程外,从公平队列中移除。
如果运行队列上进程数是0,则先通过idle_balance函数从其他CPU上调度,进行负载均衡。
对当前运行的进程prev,通过调用它所属的类的put_prev_task方法,将当前进程放入运行队列的合适位置。下图展示过程,图为公平调度类的调度方法,之后对实时调度方法的说明(idle类方法为空):
与方法put_prev_entity()方法,将当前进程加入公平调度队列。因为如果该类是公平调度类,则调度一定会在公平调度队列中有一位置,更新当前实例的状态,并入队:
其中入队位置有该值决定entity_key:
对于采用实时调度的类,调用update_curr_rt函数,并置当前进程执行开始时间是0
下图为update_curr_tr函数,
计算delta_exec值为运行队列现在的时钟值与当前进程开始值。更改当前进程的状态,修改当前实时进程的总运行时间与开始时间,对实时调度队列中的实例更新时间。
之后,在运行队列上选择下一个进程中pick_next_task函数。
对于运行队列,如果队列中进程数与公平调度队列中的进程数相同,即没有实时进程时则在公平调度队列中选择进程:
对调度类中具有最高优先级的类赋值给class,调用该类的pick_next_task方法,根据不同调度类又分为:
对于实时进程则:
对于一个实例,如果它不在实时队列组中,则返回拥有这个实例的task_struct结构为next进程并修改执行开始时间为运行队列当时钟前值。
对于公平调度进程则:
返回公平调度队列上选择不在公平调度组中的task_struct。
对于idle,返回队列中的idle task_struct结构,在调度过程中,永远不会返回NULL,因为至少有idle进程的存在。
在队列中从不同类中,选择出了将要被调度的类后,如果选择的进程next与prev不同则,进行进程的上下文切换:
修改交换次数,将next至为当前进程,进行切换。
2,执行完switch_to后,又执行了battier函数,之后又执行finish_task_switch函数
另:
struct task_struct *__switch_to(struct task_struct *prev,struct task_struct *next);
将next->thread.esp中的数据存入esp寄存器中
在switch_to宏执行后,执行ret_from_fork()函数。
执行完这个函数之后,执行include/asm-x86/system.h 下的__switch_to函数()
再执行__unlazy_fpu()函数。
3,堆栈发生切换位置,在切换堆栈前后,current_thread_info变化
对于切换堆栈,在switch_to中查找修改堆栈指针代码即可即:
图中movel %[next_sp],%%esp 即为修改堆栈指针,指向next进程的堆栈。因为在内核态中,栈顶指针减去8K偏移(两页)便可得到thread_info位置,从而,在切换后current_thread_info内容为切换后的新进程的thread_info内容。
4,地址空间发生切换,解释地址空间的切换不会影响后续切换代码的执行
切换地址空间在context_switch函数的switch_mm方法,在switch_mm中,重新加载页表即修改cr3寄存器的值:
切换地址空间发生在切换堆栈之前,不会影响后续代码执行,因为进程的切换发生在内核态,内核态地址空间是共用的。没有修改堆栈指针及其他寄存器的值,即堆栈没有变,栈内值未发生改变。
5,current宏所代表的进程发生变化的源码位置
修改该CPU的current_task为next_p,即current宏发生了改变。
6,任务状态段中关于内核堆栈的信息发生变化源码位置
Tss段在_switch_to中被声明,并被赋值:
其中,esp0即为内核堆栈栈底指针
在schedule()函数中,
首先禁止抢占,获取当前CPU,该CPU的执行队列,队列上正在执行的进程,以及该进程的交换计数信息并释放该进程占用的锁。之后,对禁止中断,更新运行队列时钟,该队列的自旋时钟加锁,后清除当前进程的thread_flag中TIF_NEED_RESCHED,
如果进程不在可运行状态,并且可被抢占,若进程处于非阻塞挂起,则将其改为可运行,否则调用deactivate_task()函数,并修改上下文交换次数。其中在deactive_task()函数中调用了denqueue_task()函数:
P进程调用属于自己调度类的dequeue_task()方法,将p从当前rp运行队列上移出。例如对于公平调度队列中的进程调用以下函数:
对p的所有实体除含有子实体的父进程外,从公平队列中移除。
如果运行队列上进程数是0,则先通过idle_balance函数从其他CPU上调度,进行负载均衡。
对当前运行的进程prev,通过调用它所属的类的put_prev_task方法,将当前进程放入运行队列的合适位置。下图展示过程,图为公平调度类的调度方法,之后对实时调度方法的说明(idle类方法为空):
与方法put_prev_entity()方法,将当前进程加入公平调度队列。因为如果该类是公平调度类,则调度一定会在公平调度队列中有一位置,更新当前实例的状态,并入队:
其中入队位置有该值决定entity_key:
对于采用实时调度的类,调用update_curr_rt函数,并置当前进程执行开始时间是0
下图为update_curr_tr函数,
计算delta_exec值为运行队列现在的时钟值与当前进程开始值。更改当前进程的状态,修改当前实时进程的总运行时间与开始时间,对实时调度队列中的实例更新时间。
之后,在运行队列上选择下一个进程中pick_next_task函数。
对于运行队列,如果队列中进程数与公平调度队列中的进程数相同,即没有实时进程时则在公平调度队列中选择进程:
对调度类中具有最高优先级的类赋值给class,调用该类的pick_next_task方法,根据不同调度类又分为:
对于实时进程则:
对于一个实例,如果它不在实时队列组中,则返回拥有这个实例的task_struct结构为next进程并修改执行开始时间为运行队列当时钟前值。
对于公平调度进程则:
返回公平调度队列上选择不在公平调度组中的task_struct。
对于idle,返回队列中的idle task_struct结构,在调度过程中,永远不会返回NULL,因为至少有idle进程的存在。
在队列中从不同类中,选择出了将要被调度的类后,如果选择的进程next与prev不同则,进行进程的上下文切换:
修改交换次数,将next至为当前进程,进行切换。
2,执行完switch_to后,又执行了battier函数,之后又执行finish_task_switch函数
另:
struct task_struct *__switch_to(struct task_struct *prev,struct task_struct *next);
将next->thread.esp中的数据存入esp寄存器中
在switch_to宏执行后,执行ret_from_fork()函数。
执行完这个函数之后,执行include/asm-x86/system.h 下的__switch_to函数()
再执行__unlazy_fpu()函数。
3,堆栈发生切换位置,在切换堆栈前后,current_thread_info变化
对于切换堆栈,在switch_to中查找修改堆栈指针代码即可即:
图中movel %[next_sp],%%esp 即为修改堆栈指针,指向next进程的堆栈。因为在内核态中,栈顶指针减去8K偏移(两页)便可得到thread_info位置,从而,在切换后current_thread_info内容为切换后的新进程的thread_info内容。
4,地址空间发生切换,解释地址空间的切换不会影响后续切换代码的执行
切换地址空间在context_switch函数的switch_mm方法,在switch_mm中,重新加载页表即修改cr3寄存器的值:
切换地址空间发生在切换堆栈之前,不会影响后续代码执行,因为进程的切换发生在内核态,内核态地址空间是共用的。没有修改堆栈指针及其他寄存器的值,即堆栈没有变,栈内值未发生改变。
5,current宏所代表的进程发生变化的源码位置
修改该CPU的current_task为next_p,即current宏发生了改变。
6,任务状态段中关于内核堆栈的信息发生变化源码位置
Tss段在_switch_to中被声明,并被赋值:
其中,esp0即为内核堆栈栈底指针
相关文章推荐
- Linux进程调度和切换过程分析
- linux 进程调度切换过程分析
- Linux进程调度和切换过程分析
- Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)
- 通过gdb跟踪进程调度分析进程切换的过程
- 初学Linux中进程调度与进程切换过程
- Linux 进程调度、切换的分析
- Linux进程调度切换和虚拟空间管理深入分析
- Linux下进程的创建过程分析(_do_fork/do_fork详解)--Linux进程的管理与调度
- Linux内核分析课程8_进程调度与进程切换过程
- Linux下进程的创建过程分析(_do_fork/do_fork详解)--Linux进程的管理与调度(八)
- Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)【转】
- Linux下进程的创建过程分析(_do_fork do_fork详解)--Linux进程的管理与调度(八)
- Linux进程启动过程分析do_execve(可执行程序的加载和运行)---Linux进程的管理与调度(十一)
- Linux内核分析:实验八--Linux进程调度与切换
- Linux进程的管理与调度(八) -- Linux下进程的创建过程分析(_do_fork/do_fork详解)
- Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)
- Linux下进程的创建过程分析(_do_fork/do_fork详解)--Linux进程的管理与调度(八)【转】
- Linux内核分析:实验八--Linux进程调度与切换
- 理解进程调度时机跟踪分析进程调度与进程切换的过程(Linux)