张雨梅   原创作品转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-10000

背景知识

一般程序运行过程中都会发生中断,发生中断时,CPU先把当前的内容保存,然后执行中断程序,中断返回时,根据保存的内容恢复现场。这次实验用一个简单的时间片轮转程序分析进程调度的过程。

实验过程

使用实验楼的虚拟机操作,实验代码在mykernel中找,包括3个c文件,mypcb.h,mymain.c,myinterrupt.c。

打开实验楼的shell,修改mykernel文件夹中的三个文件,然后make

cd LinuxKernel/linux-3.9.4
cd mykernel
vi mymain.c
vi  myinterrupt.c
vi mypcb.h
cd ..
make allnoconfig
make

运行结果的部分截图如下,可以看到此时进程3切换到进程0

代码分析

实验用到三个c文件, mypcb.h,mymain.c,myinterrupt.c。其中,

mypcb.h的主要功能:

1.PCB结构体

2.Thread结构体

3.声明了my_schedule函数

mymain.c的主要功能:

1.my_start_kernel函数创建进程0,也是入口函数

2.复制进程

3.进程函数my_process,其中有my_schedule函数的调用

myinterrupt.c的主要功能:

1.my_timer_handler产生时钟中断

2.my_schedule切换进程

下面分析程序的执行过程

mypcb.h中设置MAX_TASK_NUM的值为4,也就是共有四个进程,分别为0,1,2,3.

程序从my_start_kernel开始执行,

    int pid = 0;int i;/* Initialize process 0*/task[pid].pid = pid;/*进程0*/task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped 进程0的初始状态为可运行 */task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;/*进程0的eip指向my_process*/task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];/*初始时sp指向分配给进程0的堆栈的最高地址*/task[pid].next = &task[pid];/*初始进程0的下一个进程是进程0*/

然后复制了三个进程,注意

    task[i].state = -1;/*进程1,2,3的初始状态不可执行*/

    task[i].next = task[i-1].next;task[i-1].next = &task[i];

这两句代码设置了进程的切换方式,比如进程0切换到进程1,进程1切换到进程2。

   /* start process 0 by task[0] */pid = 0;my_current_task = &task[pid];asm volatile("movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */"pushl %1\n\t" /* push ebp */"pushl %0\n\t" /* push task[pid].thread.ip */"ret\n\t" /* pop task[pid].thread.ip to eip */"popl %%ebp\n\t":: "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/);

现在执行的是进程0,这段代码是把esp指向分配给进程0的堆栈段的最高地址,把当前ebp入栈保存,eip指向task[pid].thread.ip,也就是my_process,开始执行my_process函数。这里ret执行以后,转到my_process函数,"popl %%ebp\n\t"这一句不执行。

     if(i%10000000 == 0)
          {
              printk(KERN_NOTICE "this is process %d -\n",my_current_task->pid);
              if(my_need_sched == 1)
              {
 my_need_sched = 0;/*置为0,不能进行进程调度,直到再次发生时钟中断置为1*/
 my_schedule();
              }
 printk(KERN_NOTICE "this is process %d +\n",my_current_task->pid);          }

结合my_timer_handler函数一起分析

    if(time_count%1000 == 0 && my_need_sched != 1){printk(KERN_NOTICE ">>>my_timer_handler here<<<\n");my_need_sched = 1;/*置为1*/}

初始my_need_sched值为0,my_timer_handler函数发生时钟中断时,置my_need_sched值为1,my_process函数中满足进程调度条件,调用my_schedule()。进程0的next是进程1,初始时,进程1,2,3的state均为-1,故执行else段。也就是说初次切换到进程1,2,3时都是先执行else段的代码。

   else{next->state = 0;my_current_task = next;printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);
        /* switch to new process */asm volatile(
            "pushl %%ebp\n\t" /* save ebp */
            "movl %%esp,%0\n\t" /* save esp */
            "movl %2,%%esp\n\t" /* restore esp */
            "movl %2,%%ebp\n\t" /* restore ebp */
            "movl $1f,%1\n\t" /* save eip */
            "pushl %3\n\t"
            "ret\n\t" /* restore eip */: "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip));}

1.state:先把进程1的state置为0,下次切换到进程1的时候,就是执行if段。

2.ebp、esp:目前依然在进程0的堆栈中,先把进程0的ebp入栈保存,设置pre->thread.sp为进程0的当前esp,然后esp、ebp都指向进程1的esp,其被初始化为分配给进程1的堆栈的最高地址。ebp、esp值相同,因为进程1初次使用,堆栈为空。

3.eip:设置进程0的eip为$1f,用pre->thread.ip保存,表示if代码段1:处,即下次切换到进程0时,从1:处开始执行。eip指向eip指向进程1的eip,其被初始化为my_process。

这时即完成了进程0向进程1的切换。进程1切换进程2,进程2切换到进程3的执行过程与之类似。

当进程3切换到进程0时,由于进程0已经被执行过一次,其state为0,此时执行if代码段。

   if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */{
        /* switch to next process */asm volatile("pushl %%ebp\n\t" /* save ebp */"movl %%esp,%0\n\t" /* save esp */
       "movl %2,%%esp\n\t" /* restore esp */"movl $1f,%1\n\t" /* save eip */    "pushl %3\n\t""ret\n\t" /* restore eip */"1:\t" /* next process start here */"popl %%ebp\n\t": "=m" (prev->thread.sp),"=m" (prev->thread.ip): "m" (next->thread.sp),"m" (next->thread.ip));my_current_task = next;printk(KERN_NOTICE ">>>switch %d to %d<<<\n",prev->pid,next->pid);}

这段代码与else段代码大体过程相同,就是保存进程3的ebp,esp,eip,然后对esp,eip重新赋值。不同的是:

1.if没有设置ebp的值,此时进程0的栈非空

2.if中设置的eip为进程0的eip,也就是执行到1:处,注意此时是已经转到了进程0的堆栈中。然后执行pop %%ebp,由于进程0的栈顶元素是进程0被中断时保存的ebp,此时进程0的堆栈情况和被中断时的堆栈情况一样。

继续执行下面的代码,是在进程0的堆栈中执行的。

程序继续运行,以后发生的进程切换执行的都是if段代码,和上述过程相同。

进程切换的执行过程可以表示为

另外可以看出,进程的next,prev是变化的,当前的prev进程,在以后的进程调度中,会变成next进程。

转载于:https://www.cnblogs.com/icecri/p/4339641.html

基于时间片轮转程序分析进程调度相关推荐

  1. 基于静态类型分析的java程序函数调用图构建方法研究,JAVA的静态方法调用

    JAVA的静态方法调用 介绍一种数据流程分析,这种分析计算存在不同地址并由方法调用返回的引用.分析的结果用来估算方法调 (本文共4页) 阅读全文>> 介绍一种数据流程分析,这种分析计算存在 ...

  2. Python与OpenCV(二)——基于背景差分法的运动目标检测程序分析

    背景差分法是传统运动目标检测算法中最常用的方法.其基本原理如图所示. 从图中可知,背景差分法是通过建立背景模型,比较当前帧与背景模型对应像素的差异点来检测运动目标的方法. 背景模型的建立主要通过两种方 ...

  3. 程序分析-基于SVF实现AnderSen指针分析算法

    指针分析 一.前言 1.1.指针分析 1.2.SVF 二.AnderSen算法 2.1.AnderSen算法约束 2.2.示例 2.2.1.将C语言源代码编译为SSA形式的LLVM IR 2.2.2. ...

  4. (C++)一个按时间片轮转法实现进程调度的程序

    文章目录 前言 一.时间片轮转算法是什么? 二.使用步骤 1.题目要求 2.代码 前言 这是我大二时的操作系统实验代码,编程语言选择的是C++,编程环境是WIN10的vs2022 一.时间片轮转算法是 ...

  5. java 复杂网络分析_基于复杂网络的Java程序分析工具设计与实现思路浅谈

    基于复杂网络的Java程序分析工具设计与 实现思路浅谈 摘要:近年来,随着科学技术的进步,计算机技术发展速度的加快,使得软件价值也逐步提高,不管是软件系统的应用领域,还是其规模均获得了相应的扩大,且软 ...

  6. rsyslog概要以及源码安装mysql,rsyslog输出到Mysql基于loganalyzer查看分析

    Rsyslog:syslog的加强版本 日志对于系统中程序和服务的运行起着很大的作用,我们经常需要去看日志记录来查看各种信息,有用户登录信息,有网页访问信息,有系统故障信息,等等.. 日志通常有日志级 ...

  7. (IOS)BaiduFM 程序分析

    本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源app BaiduFM的研究心得. 项目地址:https://github.com/belm/BaiduFM-Swift 一.项 ...

  8. imx6 vpu程序分析

    imx6 vpu程序分析 背景 最近公司需要将产品与外界的设备进行流媒体通信,经过一系列的方案研究确立,最终把功能完成,目前能够顺利的播放基于h264的流媒体文件,趁着闲暇时间对相关的东西做一些笔记记 ...

  9. 人工蜂群算法python_人工蜂群算法简介与程序分析

    目前人工蜂群算法主要分为基于婚配行为与基于釆蜜行为两大类,本文研究的是基于釆蜜行为的人工蜂群算法. 蜜蜂采蜜 自然界中的蜜蜂总能在任何环境下以极高的效率找到优质蜜源,且能适应环境的改变.蜜蜂群的采蜜系 ...

最新文章

  1. 06-Windows Server 2012 R2 会话远程桌面-标准部署-RD网关(RemoteApp)
  2. django安装mysql驱动
  3. 【Linux入门到精通系列讲解】内存管理malloc和free函数
  4. 纸牌游戏CardBattle的设计与开发
  5. {dede:list}和{dede:arclist}的区别
  6. oracle中prad函数_等保测评2.0:Oracle身份鉴别
  7. 用Navicat连接mysql报错:2003-Can't connect to MySql server on '10.100.0.109'(10039)
  8. 荣耀20s真机谍照曝光:开孔全面屏+后置竖排三摄
  9. c++中的system函数
  10. python下载可执行安装程序_如何下载并安装python
  11. mysql truncate 授权_Oracle给用户授权truncatetable的实现方案
  12. 人人都能看懂的LSTMGRU
  13. 欢迎关注微信公众号Android系统攻城狮,会持续分享技术输出!!!
  14. python的flask实现接口_python+flask:实现POST接口功能
  15. 大数据Hadoop生态系统介绍
  16. 常用的工业控制计算机有哪几类,工业控制计算机在行业应用中都有哪些特点?...
  17. 实践Hive的点点滴滴
  18. C#界面控件DotNetBar使用详解
  19. 【GitHub】README.md文件中 markdown语法 插入超链接
  20. 测试VPS线路与网络的小帮手

热门文章

  1. ssl mybatis实现数据库字段的加解密
  2. IDEA实现到JSTL
  3. 【agc019F】Yes or No
  4. saltstack配置管理之YAML(二)
  5. Java技术在多数据库系统中的应用研究
  6. 转:ESRI矢量数据格式简介
  7. shell 脚本常用参数
  8. 一秒搭建gitbook
  9. CentOS6.7 安装hadoop2.7.5
  10. 《不只是美:信息图表设计原理与经典案例》—— 2.5 功能限制形式