Linux 2.4进程调度分析 3

原创 2004年05月29日 15:47:00

四. 进程切换过程

从一个进程的上下文切换到另一个进程的上下文,因为其发生频率很高,所以通常都是调度器效率高低的关键。在Linux中,这一功能是以一段经典的汇编代码实现的,此处就着力描述这段代码。

这段名为switch_to()的代码段在schedule()过程中调用,以一个宏实现:

 

/* 节选自[include/asm-i386/system.h] */

#define switch_to(prev,next,last) do {                                            /

          asm volatile("pushl %%esi/n/t" /

                         "pushl %%edi/n/t"                   /

                         "pushl %%ebp/n/t"         /保存esiediebp寄存器

                         "movl %%esp,%0/n/t"       /esp保存到prev->thread.esp

                         "movl %3,%%esp/n/t"       /next->thread.esp恢复esp

                         "movl $1f,%1/n/t"                   /prev->thread.eip中保存"1"的跳转地址,当prev被再次切换到的时候将从那里开始执行

                         "pushl %4/n/t"            /在栈上保存next->thread.eip__switch_to()返回时将转到那里执行,即进入next进程的上下文

                         "jmp __switch_to/n"       /跳转到__switch_to(),进一步处理(见下)

                         "1:/t"                                        /

                         "popl %%ebp/n/t"                    /

                         "popl %%edi/n/t"                    /

                         "popl %%esi/n/t"                    /先恢复上次被切换走时保存的寄存器值,再从switch_to()中返回。

                         :"=m" (prev->thread.esp), /%0

                      "=m" (prev->thread.eip),/%1

                          "=b" (last) /ebx,因为进程切换后,恢复的栈上的prev信息不是刚被切换走的进程描述符,因此此处使用ebx寄存器传递该值给prev

                         :"m" (next->thread.esp),  /%3

                       "m" (next->thread.eip),     /%4

                            "a" (prev), "d" (next),                    /eax,edx

                            "b" (prev));                               /ebx

} while (0)

 

 

 

进程切换过程可以分成两个阶段,上面这段汇编代码可以看作第一阶段,它保存一些关键的寄存器,并在栈上设置好跳转到新进程的地址。第二阶段在switch_to()中启动,实现在__switch_to()函数中,主要用于保存和更新不是非常关键的一些寄存器(以及IO操作许可权映射表ioperm)的值:

  • unlazy_fpu(),如果老进程在task_structflags中设置了PF_USEDFPU位,表明它使用了FPUunlazy_fpu()就会将FPU内容保存在task_struct::thread中;

  • 用新进程的esp0task_struct::thread中)更新init_tss中相应位置的esp0

  • 在老进程的task_struct::thread中保存当前的fsgs寄存器,然后从新进程的task_struct::thread中恢复fsgs寄存器;

  • 从新进程的task_struct::thread中恢复六个调试寄存器的值;

  • next中的ioperm更新init_tss中的相应内容

switch_to()函数正常返回,栈上的返回地址是新进程的task_struct::thread::eip,即新进程上一次被挂起时设置的继续运行的位置(上一次执行switch_to()时的标号"1:"位置)。至此转入新进程的上下文中运行。

在以前的Linux内核中,进程的切换使用的是far jmp指令,2.4采用如上所示的手控跳转,所做的动作以及所用的时间均与far jmp差不多,但更利于优化和控制。

 

五. 调度器:具体实现时函数的调用关系,并对各函数的基本功能进行说明

Linux的调度器主要实现在schedule()函数中。

1.调度器工作流程

schedule()函数的基本流程可以概括为四步:

1). 清理当前运行中的进程

2). 选择下一个投入运行的进程

3). 设置新进程的运行环境

4). 执行进程上下文切换

5). 后期整理

其中包含了一些锁操作:就绪队列锁runquque_lock,全局核心锁kernel_flag,全局中断锁global_irq_lock,进程列表锁tasklist_lock。下面先从锁操作开始描述调度器的工作过程。

A. 相关锁

  • runqueue_lock,定义为自旋锁,对就绪队列进行操作之前,必须锁定;

  • kernel_flag,定义为自旋锁,因为很多核心操作(例如驱动中)需要保证当前仅由一个进程执行,所以需要调用lock_kernel()/release_kernel()对核心锁进行操作,它在锁定/解锁kernel_flag的同时还在task_struct::lock_depth上设置了标志,lock_depth小于0表示未加锁。当发生进程切换的时候,不允许被切换走的进程握有kernel_flag锁,所以必须调用release_kernel_lock()强制释放,同时,新进程投入运行时如果lock_depth>0,即表明该进程被切换走之前握有核心锁,必须调用reacquire_kernel_lock()再次锁定;

  • global_irq_lock,定义为全局的内存长整型,使用clear_bit()/set_bit()系列进行操作,它与global_irq_holder配合表示当前哪个cpu握有全局中断锁,该锁挂起全局范围内的中断处理(见irq_enter());

  • tasklist_lock,定义为读写锁,保护以init_task为头的进程列表结构。

Linux 2.4进程调度分析 5

2. 调度器工作时机调度器的启动通常有两种方式:A. 主动式在核心应用中直接调用schedule()。这通常发生在因等待核心事件而需要将进程置于挂起(休眠)状态的时候--这时应该主动请求调度以方便其他...
  • thefirstwind
  • thefirstwind
  • 2004年05月29日 16:02
  • 4378

Linux 2.4进程调度分析 1

Linux 2.4 进程调度分析 内容提要:● 前言:技术的背景、特点和应用价值● 就绪进程选择算法● 相关数据结构● 调度器及其他核心应用的调度相关部分:具体实现时函数的调用关系,并对各函数的基本功...
  • thefirstwind
  • thefirstwind
  • 2004年05月29日 16:02
  • 2765

Linux 2.4进程调度分析 1

Linux 2.4 进程调度分析 内容提要:● 前言:技术的背景、特点和应用价值● 就绪进程选择算法● 相关数据结构● 调度器及其他核心应用的调度相关部分:具体实现时函数的调用关系,并对各函数的基本功...
  • sxgwy
  • sxgwy
  • 2004年12月22日 13:14
  • 501

Linux 2.4进程调度分析 6

六. 其他核心应用的调度相关部分系统中很多技术都和调度器相关,这里仅就其中几个稍作展开,并且不涉及该技术的细节,仅就其中与调度器相关的部分进行讨论,假定读者对于该技术有初步的了解。1. IDLE进程系...
  • thefirstwind
  • thefirstwind
  • 2004年05月29日 15:48
  • 2291

Linux 2.4进程调度分析 2

3. current核心经常需要获知当前在某CPU上运行的进程的task_struct,在Linux中用current指针指向这一描述符。current的实现采用了一个小技巧以获得高效的访问速度,这个...
  • thefirstwind
  • thefirstwind
  • 2004年05月29日 15:47
  • 2112

Linux 2.4进程调度分析 7

3. smp系统初始化init_task在完成关键数据结构初始化之后,在进行硬件的初始化之前,会调用smp_init()对SMP系统进行初始化。smp_init()调用smp_boot_cpus(),...
  • thefirstwind
  • thefirstwind
  • 2004年05月29日 15:48
  • 1540

Linux 2.4进程调度分析 4

B. prev在schedule中,当前进程(也就是可能被调度走的进程)用prev指针访问。对于SCHED_RR的实时进程,仅当该进程时间片结束(counter==0)后才会切换到别的进程,此时将根据...
  • thefirstwind
  • thefirstwind
  • 2004年05月29日 15:48
  • 1846

Linux 2.4进程调度分析 8

3. 实时性能Linux 2.4通过就绪进程选择算法的设计区分实时进程和非实时进程,只要有实时进程可运行,非实时进程就不会获得运行机会。Linux又将实时进程分为SCHED_RR和SCHED_FIFO...
  • thefirstwind
  • thefirstwind
  • 2004年05月29日 15:48
  • 1970

Linux进程调度分析

Linux进程调度分析
  • Li_Ning_
  • Li_Ning_
  • 2016年06月12日 15:50
  • 1496

内核源码分析之进程调度机制

转自:http://www.cnblogs.com/liangning/p/3892306.html 进程调度所使用到的数据结构: 1.就绪队列 内核为每一个cpu创建一个进程就...
  • caoyahong114
  • caoyahong114
  • 2016年06月16日 14:52
  • 1703
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Linux 2.4进程调度分析 3
举报原因:
原因补充:

(最多只允许输入30个字)