一个简单的时间片轮转多道程序

  • 操作系统的“两把剑”:中断上下文(保存现场和恢复现场)和进程上下文的切换

源代码的分析

*使用的源代码为视频中所使用的精简内核的源代码

首先分析mypcd.h

  1. struct Thread {
  2. unsigned long ip;//point to cpu run address
  3. unsigned long sp;//point to the thread stack's top address
  4. //todo add other attrubte of system thread
  5. };

这是一个进程的结构体,其中的ip标记的是该进程的eip,而sp标记的是该进程的esp。

  1. typedef struct PCB{
  2. int pid; // 进程的id
  3. volatile long state; /*进程的状态 -1 unrunnable, 0 runnable, >0 stopped */
  4. char stack[KERNEL_STACK_SIZE];//进程的堆栈 each pcb stack size is 1024*8
  5. /* CPU-specific state of this task */
  6. struct Thread thread;
  7. unsigned long task_entry;//进程的入口
  8. struct PCB *next;//以链表的形式连接下一个PCB
  9. unsigned long priority;//进程的优先度////////
  10. //todo add other attrubte of process control block
  11. }tPCB;

这是一个进程控制块的结构体,记录着进程的的各种状态与信息。

  1. void my_schedule(void);

该函数的功能为调度器,用于进程之间的调度运行。

接着分析mymain.c即进程的启动机制

  1. void __init my_start_kernel(void)
  2. {
  3. int pid = 0;
  4. /* Initialize process 0*/
  5. task[pid].pid = pid;
  6. task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
  7. // set task 0 execute entry address to my_process
  8. task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
  9. task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
  10. task[pid].next = &task[pid]; 因为系统中仅有一个进程(初始化),所以下一个进程指向自己
  11. /*fork more process */
  12. for(pid=1;pid<MAX_TASK_NUM;pid++) 创建进程
  13. {
  14. memcpy(&task[pid],&task[0],sizeof(tPCB));
  15. task[pid].pid = pid;
  16. task[pid].state = -1;
  17. task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
  18. task[pid].priority=get_rand(PRIORITY_MAX);//each time all tasks get a random priority
  19. }
  20. task[MAX_TASK_NUM-1].next=&task[0];
  21. printk(KERN_NOTICE "\n\n\n\n\n\n system begin :>>>process 0 running!!!<<<\n\n");
  22. /* start process 0 by task[0] */
  23. pid = 0;
  24. my_current_task = &task[pid];
  25. asm volatile(
  26. "movl %1,%%esp\n\t" /* set task[pid].thread.sp to esp */
  27. "pushl %1\n\t" /* push ebp */
  28. "pushl %0\n\t" /* push task[pid].thread.ip */
  29. "ret\n\t" /* pop task[pid].thread.ip to eip */
  30. "popl %%ebp\n\t"
  31. :
  32. : "c" (task[pid].thread.ip),"d" (task[pid].thread.sp) /* input c or d mean %ecx/%edx*/
  33. );
  34. }
  • 首先是先创建第一个进程,定义其进程编号为0,状态为可执行,设置进程入口的地址为该进程的eip,设置进程的堆栈为该进程的堆栈,task[0].next=&task[0];因为此时系统之中仅仅只要一个进程,该进程的下一个进程就只能暂且指向自己了。
  • 接着是创建更多的进程,步骤是通过一个for循环进程依次创建进程的编号、状态和堆栈。将新创建的进程加入上一个进程的尾部。
  • 接下来就是要启动0号进程也就是第一个进程,将该进程的堆栈标记so存入寄存器esp中,紧接着将ebp压入栈中,再将该进程的eip压入栈中,接着将myprocess的eip弹出给eip,也就是启动0号进程。
  • 当运行完后则需要运行popl %ebp退回上一状态。

*1f指的是标号1所在的位置。

  1. void my_process(void)
  2. {
  3. int i = 0;
  4. while(1)
  5. {
  6. i++;
  7. if(i%10000000 == 0)
  8. {
  9. if(my_need_sched == 1)
  10. {
  11. my_need_sched = 0;
  12. sand_priority();
  13. my_schedule();
  14. }
  15. }
  16. }
  17. }//end of my_process
  • 在myprocess的函数之中输出时采用了主动调度,循环1000万次才有一次机会判断一下是否需要调度。
  • 调度完切换回来时,则从myschedule();函数继续执行。

最后分析myinterrupt.c即进程的切换机制

  1. void my_timer_handler(void)
  2. {
  3. #if 1
  4. // make sure need schedule after system circle 2000 times.
  5. if(time_count%2000 == 0 && my_need_sched != 1)
  6. {
  7. my_need_sched = 1;
  8. //time_count=0;
  9. }
  10. time_count ++ ;
  11. #endif
  12. return;
  13. }
  • 该函数的作用是设置时间片的大小,时间片用完时设置一下调度标志,当进程调度发现状态为1时,则会执行一次my_process,而后执行一次my_schedule。

    1. void my_schedule(void)
    2. {
    3. tPCB * next;
    4. tPCB * prev;
    5. // if there no task running or only a task ,it shouldn't need schedule
    6. if(my_current_task == NULL
    7. || my_current_task->next == NULL)
    8. {
    9. printk(KERN_NOTICE " time out!!!,but no more than 2 task,need not schedule\n");
    10. return;
    11. }
    12. /* schedule */
    13. next = get_next();
    14. prev = my_current_task;
    15. printk(KERN_NOTICE " the next task is %d priority is %u\n",next->pid,next->priority);
    16. if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
    17. {//save current scene
    18. /* switch to next process */
    19. asm volatile(
    20. "pushl %%ebp\n\t" /* save ebp */
    21. "movl %%esp,%0\n\t" /* save esp */
    22. "movl %2,%%esp\n\t" /* restore esp */
    23. "movl $1f,%1\n\t" /* save eip */
    24. "pushl %3\n\t"
    25. "ret\n\t" /* restore eip */
    26. "1:\t" /* next process start here */
    27. "popl %%ebp\n\t"
    28. : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
    29. : "m" (next->thread.sp),"m" (next->thread.ip)
    30. );
    31. my_current_task = next;//switch to the next task
    32. printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n",prev->pid,next->pid,next->pid);
    33. }
    34. else
    35. {
    36. next->state = 0;
    37. my_current_task = next;
    38. printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n\n",prev->pid,next->pid,next->pid);
    39. /* switch to new process */
    40. asm volatile(
    41. "pushl %%ebp\n\t" /* save ebp */
    42. "movl %%esp,%0\n\t" /* save esp */
    43. "movl %2,%%esp\n\t" /* restore esp */
    44. "movl %2,%%ebp\n\t" /* restore ebp */
    45. "movl $1f,%1\n\t" /* save eip */
    46. "pushl %3\n\t"
    47. "ret\n\t" /* restore eip */
    48. : "=m" (prev->thread.sp),"=m" (prev->thread.ip)
    49. : "m" (next->thread.sp),"m" (next->thread.ip)
    50. );
    51. }
    52. return;
    53. }//end of my_schedule
  • 当前进程的下一个进程赋给next,当前进程赋给prev。如果下一个进程状态为0的话,即正在执行,则需要在两个正在运行的进程之间做进程上下文切换:

    • 保存当前进程的ebp
    • 将当前进程的esp赋给prev->thread.sp,从而将其保存起来
    • 将下一个进程的sp放置esp中
    • 保存当前进程的eip至当前进程所属的pcb中
    • 将下一个进程的eip保存至当前栈中
    • 通过ret,即可调用下一个进程
  • 若下一个进程的状态还未执行过,则进行以下步骤:

    • 将下一个进程设置为运行时状态,并将其设置为当前进程
    • 保存当前进程的ebp,将其压入栈中
    • 保存当前进程的esp至当前进程的pcb中
    • 由于该程序并未执行过,所以它的堆栈为空栈,空栈的创建则是通过将ebp和esp都赋予同一个值。
    • 保存当前进程的eip
    • 将当前进程的eip压入栈中
    • 通过ret,即可调用下一个进程

操作系统是如何工作的

  • 操作系统是控制应用程序执行和充当硬件系统和应用程序之间的界面的软件。
  • 操作系统是一个大型、复杂的系统软件,它负责计算机的全部软、硬件资源的分配、调度工作,控制和协调并发活动,实现信息的存取和保护。它提供用户接口,使用户获得良好的工作环境。操作系统使整个计算机系统实现了高效率和自动化,是现代计算机系统最关键和最核心的软件系统。
  • 操作系统能够完成程序创建、程序执行、、I/O设备访问、控制对文件的访问、系统访问、查错和纠错、簿记。
  • 操作系统的核心任务之一就是管理各种可获得的资源以及合理地调度它们。
  • 操作系统保存了一些队列,每个队列都是一组等待某些资源的进程。
    • 短程队列是由在主存中并处于就绪状态的进程组成。这些进程中的任何一个进程都可以作为处理器的下一个选用者,具体选择哪个进程由分派程序决定。(中断上下文)
    • 长程队列是一列等待使用系统的新进程。操作系统通过将一个进程从长程队列转移到短程队列,向系统增加任务。每个I/O设备都有一个I/O队列。等待使用某个设备的所有进程都排在该设备队列中。当中断发生时,操作系统得到处理器的控制权。一个进程可能会求助于操作系统提供的一些服务。在这种情况下,服务调度处理程序成为进入操作系统的入口。

实验楼截图

池彬宁 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

linux内核分析--操作系统是如何工作的?的更多相关文章

  1. Linux内核分析——操作系统是如何工作的

    万子惠 + 原创作品转载请注明出处 + <Linux内核分析> 实验部分 使用实验楼的虚拟机打开shell 然后cd mykernel 您可以看到qemu窗口输出的内容的代码mymain. ...

  2. Linux内核分析— —操作系统是如何工作的(20135213林涵锦)

    mykernel实验指导(操作系统是如何工作的) 实验要求 运行并分析一个精简的操作系统内核,理解操作系统是如何工作的 使用实验楼的虚拟机打开shell cd LinuxKernel/linux-3. ...

  3. Linux内核分析--操作系统是如何工作的

    “平安的祝福 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 一.初 ...

  4. 20135239益西拉姆 Linux内核分析 操作系统是怎样工作的?

    益西拉姆+ 原创作品+ <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” 堆栈 堆栈是C语言程序运行时 ...

  5. LInux内核分析——计算机是如何工作的进行

    万子惠 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 " 实 ...

  6. linux内核分析--计算机是如何工作的

    实验部分 使用gcc -S -o main.s main.c -m32命令将源代码编译成汇编代码. 源代码如下: int g(int x) { return x + 9; } int f(int x) ...

  7. Linux内核分析— —计算机是如何工作的(20135213林涵锦)

    实验部分 (以下命令为实验楼64位Linux虚拟机环境下适用,32位Linux环境可能会稍有不同) 使用 gcc –S –o main.s main.c -m32 命令编译成汇编代码, int g(i ...

  8. Linux内核分析——计算机是如何工作的

    马悦+原创作品转载请注明出处+<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.计算机是如何工作的 ( ...

  9. Linux内核分析 计算机是如何工作的——by王玥

    1.冯诺依曼体系结构:也就是指存储程序计算机 硬件(存储程序计算机工作模式): 软件(程序员角度): 2.API:程序员与计算机的接口界面 ABI:程序与CPU的接口界面 3.X86的实现: 4.X8 ...

随机推荐

  1. 团队作业——Alpha冲刺 5/12

    团队作业--Alpha冲刺 冲刺任务安排 杨光海天 今日任务:编辑界面完成部分内容,学习了下拉菜单控件的建立,完善界面标题内容,以及交互. 明日任务:继续完善编辑界面,学习使用gallery,着手配图 ...

  2. JMETER TPS

    上一节中,我们了解了jmeter的一此主要元件,那么这些元件如何使用到性能测试中呢.这一节创建一个简单的测试计划来使用这些元件.该计划对应的测试需求. 1)测试目标网站是fnng.cnblogs.co ...

  3. PyQt5--Signal&Slot

    # -*- coding:utf-8 -*- ''' Created on Sep 14, 2018 @author: SaShuangYiBing ''' import sys from PyQt5 ...

  4. 关于Maven配置的一些标签含义(后续逐渐补充)

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  5. oracle 查看删除重复数据

    1.查询重复数据select * from 表名 where 重复字段(一般为主键)in (select 重复字段 from 表名 group by 重复字段 having count(WF_OID) ...

  6. leetcode349—Intersection of Two Arrays

    Given two arrays, write a function to compute their intersection. Example 1: Input: nums1 = [1,2,2,1 ...

  7. ViewData、ViewBag、TempData、Session的区别与联系

    简介 这篇文章是我在学习ASP.NET MVC程序传值方式梳理总结的笔记.在ASP.NET MVC中,页面间和Controller与View之间主要有以下几种小量数据传值方式, ViewData.Vi ...

  8. C++箴言:理解typename的两个含义 nested dependent name(嵌套依赖名字)

    template<class T> class Widget; // uses "class"template<typename T> class Widg ...

  9. 报错Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA

    解决方法:import os                  os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'输入1:显示所有信息 2:只显示warning和erro ...

  10. 大数据入门第十四天——Hbase详解(三)hbase基本原理与MR操作Hbase

    一.基本原理 1.hbase的位置 上图描述了Hadoop 2.0生态系统中的各层结构.其中HBase位于结构化存储层,HDFS为HBase提供了高可靠性的底层存储支持, MapReduce为HBas ...