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

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

源代码的分析

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

首先分析mypcd.h

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

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

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

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

void my_schedule(void);

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

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

void __init my_start_kernel(void)
{
int pid = 0;
/* Initialize process 0*/
task[pid].pid = pid;
task[pid].state = 0;/* -1 unrunnable, 0 runnable, >0 stopped */
// set task 0 execute entry address to my_process
task[pid].task_entry = task[pid].thread.ip = (unsigned long)my_process;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].next = &task[pid]; 因为系统中仅有一个进程(初始化),所以下一个进程指向自己
/*fork more process */
for(pid=1;pid<MAX_TASK_NUM;pid++) 创建进程
{
memcpy(&task[pid],&task[0],sizeof(tPCB));
task[pid].pid = pid;
task[pid].state = -1;
task[pid].thread.sp = (unsigned long)&task[pid].stack[KERNEL_STACK_SIZE-1];
task[pid].priority=get_rand(PRIORITY_MAX);//each time all tasks get a random priority
}
task[MAX_TASK_NUM-1].next=&task[0];
printk(KERN_NOTICE "\n\n\n\n\n\n system begin :>>>process 0 running!!!<<<\n\n");
/* 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,状态为可执行,设置进程入口的地址为该进程的eip,设置进程的堆栈为该进程的堆栈,task[0].next=&task[0];因为此时系统之中仅仅只要一个进程,该进程的下一个进程就只能暂且指向自己了。
  • 接着是创建更多的进程,步骤是通过一个for循环进程依次创建进程的编号、状态和堆栈。将新创建的进程加入上一个进程的尾部。
  • 接下来就是要启动0号进程也就是第一个进程,将该进程的堆栈标记so存入寄存器esp中,紧接着将ebp压入栈中,再将该进程的eip压入栈中,接着将myprocess的eip弹出给eip,也就是启动0号进程。
  • 当运行完后则需要运行popl %ebp退回上一状态。

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

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

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

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

      void my_schedule(void)
    {
    tPCB * next;
    tPCB * prev;
    // if there no task running or only a task ,it shouldn't need schedule
    if(my_current_task == NULL
    || my_current_task->next == NULL)
    {
    printk(KERN_NOTICE " time out!!!,but no more than 2 task,need not schedule\n");
    return;
    }
    /* schedule */ next = get_next();
    prev = my_current_task;
    printk(KERN_NOTICE " the next task is %d priority is %u\n",next->pid,next->priority);
    if(next->state == 0)/* -1 unrunnable, 0 runnable, >0 stopped */
    {//save current scene
    /* 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;//switch to the next task
    printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n",prev->pid,next->pid,next->pid); }
    else
    {
    next->state = 0;
    my_current_task = next;
    printk(KERN_NOTICE " switch from %d process to %d process\n >>>process %d running!!!<<<\n\n\n",prev->pid,next->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)
    );
    }
    return;
    }//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. U-Mail:如何实现EDM的个性化和定制化?

    设想一下,一个上班族一天要接到多少垃圾邮件?据媒体报道,目前来往的邮件中,高达95%以上的是垃圾邮件,而且有些垃圾邮件还会故意占据着邮箱的最前列.同时,随着人们接受资讯越来越快捷便利,渠道越来越多,也 ...

  2. html简单介绍(一)

    什么是html HTML 是用来描述网页的一种语言.HTML 指的是超文本标记语言 (Hyper Text Markup Language)HTML 不是一种编程语言,而是一种标记语言 (markup ...

  3. ajax知识点及正则表达式总结

    一.JSON JSON是JavaScript  Object  Notation 的首字母缩写,单词的意思是javascript对象表示法,这里说的json指的是类似于javascript对象的一种数 ...

  4. android 7.0以上共享文件(解决调用系统照相和图片剪切出现的FileUriExposedException崩溃问题)

    在android7.0开始试共享“file://”URI 将会导致引发 FileUriExposedException. 如果应用需要与其他应用共享私有文件,则应该使用 FileProvider, F ...

  5. WorldWind源码剖析系列:经纬度格网类LatLongGrid

    经纬度格网类LatLongGrid继承自可渲染对象类RenderableObject,是WorldWind中用来在星球外表绘制经纬度格网的封装类.其类图如下所示. 绘制经纬网格的主体函数为Render ...

  6. gettimeofday()函数来得到时间

    gettimeofday()函数的使用方法: 1.简介: 在C语言中可以使用函数gettimeofday()函数来得到时间.它的精度可以达到微妙 2.函数原型: #include<sys/tim ...

  7. jmeter测试webservice接口

    webservice怎样使用jmeter测试呢? 测试样例url=http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx ...

  8. JAVA框架 Spring AOP底层原理

    一:AOP(Aspect Oriented Programming)面向切面编程. 底层实现原理是java的动态代理:1.jdk的动态代理.2.spring的cglib代理. jdk的动态代理需要被代 ...

  9. CAN2.0A 和CAN2.0B

    CAN2.0A 和CAN2.0B 原创 2015年08月03日 16:03:08 3969 CAN2.0A 是CAN协议的PART A部分,此部分定义了11bit的标识区 .CAN2.0B 是CAN协 ...

  10. RocEDU.课程设计2018 第六组 第三周进展 博客补交

    RocEDU.课程设计2018第六组 第三周进展 博客补交 小组成员:20155211解雪莹,20155217杨笛,20155227辜彦霖 计划完成任务:完成课设所有内容 实际完成任务:跑通老师uco ...