进程调度

怎甘沉沦 提交于 2020-01-30 23:56:02

创建新的进程

  Linux系统中创建新进程使用fork()系统调用.所有进程都是通过复制进程0而得到的,都是进程0的子进程.
  在创建新进程的过程中,系统首先在任务数组中找出一个还没有被任何进程使用的空项(空槽).如果系统已经有64个进程在运行,则fork()系统调用会因为任务数组表没有可用空项而错误返回.然后系统为新建进程在主内存区中申请一页内存来存放其任务数据结构信息,并复制当前任务数据结构中的所有内容作为新进程任务数据结构的模板.为了防止这个还未处理完成的新建进程被调度函数执行,此时应该立刻缉拿该新进程状态置为不可中断的睡眠状态.
  随后对复制的任务数据结构进行修改.把当前进程设置为新进程的父进程,清楚信号位图并复位新进程各统计值,并设置初始运行时间片值为15个系统滴答数(150ms).解着根据当前进程设置任务状态段(TSS)中各寄存器的值.由于创建进程时新进程返回值应为0,所以需要设置tss.eax=0.新建进程内核堆栈指针tss.esp被设置成新进程任务数据结构所在内存页面的顶端,而堆栈段tss.ss0被设置成内核数据段选择符.tss.ldt被设置为局部表描述符在GDT中的索引值.如果当前进程使用了协处理器,则还需要把协处理器的完整状态保存到新进程的tss.i387结构中.
  此后系设置新任务的代码和数据段基址,限长并复制当前进程内存分页管理的页表.如果父进程有文件是打开的,则应将对应的文件的打开次数增1,解着在GDT中设置新任务的TSS和LDT描述符项,其中基地址信息指向新进程任务结构中的tss和ldt.最后再将新任务设置成可运行状态并返回新进程号.

进程调度

&&emsp由前面的描述可知,Linux进程是抢占式的.被抢占的进程仍处于TASK_RUNNING状态,指示暂时没有被CPU运行.进程的抢占式发生在进程处于用户态执行阶段,在内核执行时不能被抢占.

  为了能让进程有效地使用系统资源,又能使进程有较快的相应时间,就需要对进程的切换调度采用一定的调度策略.在Linux0.11中采用了基于优先级排队的调度策略.

调度程序

  schdule()函数首先扫描任务数组.通过比较每个就绪态(TASK_RUNNING)任务的运行时间递减滴答计数counter的值来确定当前哪个进程运行的时间最少.哪一个的值大,就表示运行时间还不长,于是就选中该进程,并使用任务切换宏函数切换带该进程运行.
  如果此时所有处于TASK_RUNNING状态进程的时间片都已经用完,系统就会根据每个进程有限权值priority,对系统中所有进程(包括正在睡眠的进程)重新计算每个任务需奥运行的时间片值counter.计算的公式是counter=counter/2 + prioity.
  然后schedule()函数重新扫描任务数组中所有处于TASK_RUNNING状态,重复上述过程,直到选择出一个进程为止.最后调用switch_to()执行实际的进程切换操作.
  如果此时没有其他进程可运行,系统就会选择进程0运行.对于Linux0.11来说,进程0会调用pause()把自己置为可中断的睡眠状态并再次调用schdule().不过在调度进程运行时,schedule()并不在意进程0处于什么状态.只要系统空闲就调度进程0运行.

进程切换

  执行实际进程切换的任务由switch_to()宏定义的一段汇编代码完成.在进行切换前,switch_to()首先检查要切换到的进程是否为当前进程,如果是则什么也不做,直接退出.否则就首先把内核全局变量current置为新任务的指针,然后长跳转到新任务的任务状态段TSS组成的地址处,造成CPU执行任务切换操作.此时CPU会把其所有寄存器的状态保存到当前任务寄存器TR中TSS段选择符所指向的当前进程任务数据结构的tss结构的tss结构中,然后把新任务段选择符所指向的新任务数据结构中tss结构中的寄存器信息恢复到CPU中,系统就正式开始运行新切换的任务了.

终止进程

  当一个进程结束了运行或在中途终止了运行,那么内核就需要释放该进程所占用的系统资源.这包括运行时所打开的文件,申请的内存等.

  当一个用户程序调用exit()系统调用时,就会执行内核函数do_exit().该函数会首先释放进程代码段和数据段占用的内存页面,关闭进程打开着的所有文件,对进程使用的当前工作目录,根目录和运行程序的i节点进行同步操作.如果进程有子进程,则让init进程作为其所有子进程的父进程.如果进程是一个会话进程并且有控制终端,则释放控制终端,并向属于该会话的所有进程发送挂断信号SIGHUP,这通常会终止该会话中的所有进程.然后把进程状态置为僵死状态TASK_ZONMBIE.并向其原父进程发送SIGHLD信号,通知某个子进程已经终止.最后do_exit()调用调用函数去执行其他进程.由此可见在进程被终止时,它的任务数据结构仍然保留着.因为其父进程还需要使用其中的信息.
  在子进程执行期间,父进程通常使用wait()或waitpid()函数等待其某个子进程该终止.当等待的子进程被终止并处于僵死状态时,父进程就会把子进程运行所使用的是按累加到自己进程中.最终释放已终止子进程任务数据结构所占用的内粗页面,并置空子进程在任务数组中的占用的指针项.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!