wangchao.org
添加收藏 | 博客
 
购物视频论坛IT业界自然风光美女图片王朝网络小游戏BT下载生活百科编程设计手机图片小说
 
笑话 | 水库 | 娱乐 | 体育 | 英语 | 宠物 | 美食 | 旅游 | 养生 | 手机 | 数码 | 汽车 | 珠宝 | 美容 | 装修 | 厨房 | 科普 | 动物 | 植物 | 影音 | 百科 | 知道 | 词典
  
 
 您好! 您现在位于: 王朝网络 → 编程设计 → 《Linux 2.4进程调度分析 3返回上一页 
 
1楼 

Linux 2.4进程调度分析 3

  网上购物、在线购物、购物搜索 欢迎光临本站购买图书、影视、音乐、数码、百货,手机等商品。

  四. 进程切换过程
  从一个进程的上下文切换到另一个进程的上下文,因为其发生频率很高,所以通常都是调度器效率高低的关键。在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" \保存esi、edi、ebp寄存器
   "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_struct的flags中设置了PF_USEDFPU位,表明它使用了FPU,unlazy_fpu()就会将FPU内容保存在task_struct::thread中;
  用新进程的esp0(task_struct::thread中)更新init_tss中相应位置的esp0;
  在老进程的task_struct::thread中保存当前的fs和gs寄存器,然后从新进程的task_struct::thread中恢复fs和gs寄存器;
  从新进程的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中,这一功能是以一段经典的汇编代码实现的,此处就着力描述这段代码。 这段名为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" \保存esi、edi、ebp寄存器 "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_struct的flags中设置了PF_USEDFPU位,表明它使用了FPU,unlazy_fpu()就会将FPU内容保存在task_struct::thread中; 用新进程的esp0(task_struct::thread中)更新init_tss中相应位置的esp0; 在老进程的task_struct::thread中保存当前的fs和gs寄存器,然后从新进程的task_struct::thread中恢复fs和gs寄存器; 从新进程的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  分析  进程调度  
 
您可以将本页贴到其他网站
UBB代码HTML代码
 
 
 
 
手机图片下载手机图片下载手机图片下载手机图片下载手机图片下载手机图片下载更多图铃
 
 
 
 
 
 
 更多内容
 ·Linux 2.4进程调度分析 2 ·Linux 2.4进程调度分析 1 ·Linux2.4.18内核下基于LKM的系统 ·Win32系统服务说明
 ·做一个Windows窗体版的DOS分析器 ·Windows XP SP2之初体验 ·我说windows和linux ·基于PXE启动的WINDOWS 2000/XP的
 ·基于PXE启动的WINDOWS 2000/XP的 ·基于PXE启动的WINDOWS 2000/XP的 ·基于PXE启动的WINDOWS 2000/XP的 ·共享Linux 和Windows Server 200
 ·UNIX上的“游戏修改器” ·unix上防止程序死锁的一些手段 ·将错误信息记录到Windows日志中. ·在IE浏览器中使用Windows窗体控件
 ·在IE浏览器中使用Windows窗体控件 ·在IE浏览器中使用Windows窗体控件 ·从"在 Internet Explorer 中 ·Windows Server操作系统和本地环
 ·用WinRAR制作安装程序 ·负载均衡更高效 —— Windows Se ·Windows 2000 的注册表备份和恢复 ·Linux下Apache服务器的配置!
 
 
 
最新评论  点此查看所有评论
 
 
 
 
发表评论(支持UBB码)


验证码:  
 
 
 
 
© 2005- 王朝网络 版权所有